#uses "LIB_SubSystem" // 调用子系统库  LIB_SubSystem.ctl
const int G_DPSET_ONLY_SEND = 1;
const int G_DPSET_TIME_EXPIRED = 2;
const int G_DPSET_DONE = 3;
const int G_DPSET_ERROR = 4;

/*
      string sURL —— 目标计算机的IP地址或者域名 dns name or ip address
      int timeOut —— 设定的超时时间,seconds.
      bool &isConnected —— 返回是否连接成功
      int &delayTime —— 返回实际的网络延迟时间
*/
LIB_Ping_Single(string sURL, int timeOut, bool &isConnected, int &delayTime)
{
  dyn_string dsOut;
  dyn_int di;
  string sIP;
  string out;
  string strCMD; // OS操作系统的系统指令。

  if (_WIN32)
  {
    // n:要发送的回显请求数。
    // w:等待每次回复的超时时间(毫秒)。
    //
    strCMD  = "chcp 437 && ping -n 1 -w 2000 -4 " + sURL;

  }
  else if (_UNIX)
  {
    strCMD = "ping -c 1 -W 2000 -4 " + sURL;
  }

  system(strCMD, out);

  out = recode(out, "GB2312"); // 转码为UTF8

  if (strpos(out, "Lost = 0 (0% loss)") >= 0)
  {

    isConnected = true;
    dsOut = strsplit(out, "\n");

    for (int i = 1; i <= dynlen(dsOut); i++)
    {
      // 找到含有延时的那一行字符串，  特征：  Minimum = 16ms, Maximum = 16ms, Average = 16ms
      if (strpos(dsOut[i], "Average") >= 0)
      {
        // 解析数值
        int Minimum, Maximum, Average;
        sscanf(dsOut[i], "    Minimum = %dms, Maximum = %dms, Average = %dms", Minimum, Maximum, Average);
        // DebugN(out,Minimum,Maximum,Average);
        delayTime = Average;
        break;
      }
    }
  }

}

// direction: "forward", "backword", "upward", "downward"
// 返回中文名nodeNameCN，
string LIB_pt_get_direction(string filenameNow,
                            string direction,
                            string &newFilename,
                            uint &newPanelNumber,
                            uint &newPermissionBit,
                            uint &newPos,
                            string &newNodename,
                            string &newNodenameCN
                           )
{

  if (direction == "")
  {
    return "";
  }

  if (filenameNow == "")
  {
    return "";
  }

  string systemName  ;

  if (globalExists("gSys")) // gSys: MEP, not MEP:
  {
    systemName = gSys;
  }
  else
  {
    systemName = getSystemName();
  }

  strreplace(systemName, ":", "");

  dyn_langString nodeName = g_PanelTopologyCache[ systemName + ":_PanelTopology.nodeName:_online.._value"];
  dyn_uint panelNumber = g_PanelTopologyCache[ systemName + ":_PanelTopology.panelNumber:_online.._value"];
  dyn_uint parentNumber = g_PanelTopologyCache[ systemName + ":_PanelTopology.parentNumber:_online.._value"];
  dyn_uint backwardPanel = g_PanelTopologyCache[ systemName + ":_PanelTopology.backwardPanel:_online.._value"];
  dyn_uint forwardPanel = g_PanelTopologyCache[ systemName + ":_PanelTopology.forwardPanel:_online.._value"];
  dyn_uint downwardPanel = g_PanelTopologyCache[ systemName + ":_PanelTopology.downwardPanel:_online.._value"];
  dyn_uint upwardPanel = g_PanelTopologyCache[ systemName + ":_PanelTopology.upwardPanel:_online.._value"];
  dyn_string fileName = g_PanelTopologyCache[ systemName + ":_PanelTopology.fileName:_online.._value"];
  dyn_uint permissionBit = g_PanelTopologyCache[ systemName + ":_PanelTopology.permissionBit:_online.._value"];

  // 获得当前画面所对应的position
  int pos = dynContains(fileName, filenameNow);

  if (pos == 0) return "";

  uint newpos;

  if (direction == "backward")
  {
    newpos = backwardPanel[pos];
  }
  else if (direction == "forward")
  {
    newpos = forwardPanel[pos];
  }
  else if (direction == "upward")
  {
    newpos = upwardPanel[pos];
  }
  else if (direction == "downward")
  {
    newpos = downwardPanel[pos];
  }

  if (newpos == 0)
  {
    return "";
  }

  // newFilename
  newFilename = fileName[newpos];
  // newPanelNumber
  newPanelNumber = panelNumber[newpos];
  // newPermissionBit
  newPermissionBit = permissionBit[newpos];
  // newNodename
  newNodename = nodeName[newpos];
  // newNodenameCN

  // 最低优先级：用原始的文件名字符串。
  newNodenameCN = nodeName[newpos];

  // 较高的优先级：用PanalTopologyChinese.cat 找一下翻译。如果有，采用。适合单机版开发（无法做分布式）
  if (strpos(getCatStr("PanalTopologyChinese", nodeName[newpos]), "not found") == -1)  // FOUND!
  {
    newNodenameCN = getCatStr("PanalTopologyChinese", nodeName[newpos]);
  }

  // 最高优先级：去子系统专用的 PanalTopologyChinese_WWT.cat 找一下翻译，如果有，则覆盖之前的设置，最终采纳。
  if (strpos(getCatStr("PanalTopologyChinese" + "_" + gSys, nodeName[newpos]), "not found") == -1)   //  FOUND!
  {
    newNodenameCN = getCatStr("PanalTopologyChinese" + "_" + gSys, nodeName[newpos]);
  }


  return "ok";
}

// 设备点击弹窗后，加载CMD,STATE,ERROR等字段。
// YGap: g_posY += 8;

synchronized void LIB_CMD_STATE_SHOW(string CMD_STATE)
{

  string dpType =  dpTypeName(dp);

  // 获得 dpe 列表
  dyn_string dsDps, dsDpsNoValue;

  // 获得dpe的列表有两种方式：
  // 优先从 DPE.CMD.这个结构体的描述中获得（通过导入点位的时候进行赋值，是由Excel定义的顺序。）。
  // 若不存在，则从系统当前进行检索，按字母顺序排序。
  if (!dpExists(dp + "." + CMD_STATE))
  {
    return;
  }

  string sDescription = dpGetDescription(dp + "." + CMD_STATE);
  dyn_string dsConfig2 =  jsonDecode(sDescription);

  if (dynlen(dsConfig2) > 0)
  {
    for (int i = 1; i <= dynlen(dsConfig2); i++)
    {
      dsDps[i] = dp + "." + CMD_STATE + "." + dsConfig2[i] + ".VALUE" ; //
      dsDpsNoValue[i] = dp + "." + CMD_STATE + "." + dsConfig2[i]   ; //dsDpsNoValue是不含.VALUE的DPE, 用于读取dp的描述
    }
  }
  else
  {
    dsDps = dpNames(dp + "." + CMD_STATE + ".*.VALUE", dpType);//STATE
    dsDpsNoValue = dpNames(dp + "." + CMD_STATE + ".*", dpType);//STATE
  }

  g_posY += 10;

  // 遍历DPE列表
  for (int i = 1; i <= dynlen(dsDps); i++)
  {
    string dpe = dsDps[i]  ; //
    string dpeNoValue = dsDpsNoValue[i];

    // 检查地址是否存在？不存在则不显示
    if (!dpExists(dpe + ":_address.._type"))
    {
      continue;
    }

    // 如果地址属性不存在，弹窗里面不显示这个报警
    int _type;
    dpGet(dpe + ":_address.._type", _type);

    if (_type == DPCONFIG_NONE)
    {
      continue;
    }

    // 如果地址未激活，弹窗里面不显示这个点位
    int _active;
    dpGet(dpe + ":_address.._active", _active);

    if (!_active)
    {
      continue;
    }


    string stringfileName;

    if (CMD_STATE == "CMD")
    {

      if (dpElementType(dpe) == DPEL_BOOL)
      {

        // 读取 dsDpsNoValue 的描述，PULSE_OPEN, PULSE_CLOSE
        if (dpGetDescription(dpeNoValue) == "PULSE_OPEN")
        {
          stringfileName = "objects/LIB/faceplate/CMD/button_pulse_open.xml";
        }
        else if (dpGetDescription(dpeNoValue) == "PULSE_CLOSE")
        {
          stringfileName = "objects/LIB/faceplate/CMD/button_pulse_close.xml";
        }
        else
        {
          stringfileName = "objects/LIB/faceplate/CMD/button_switch_2.xml";
        }
      }
      else
      {
        stringfileName = "objects/LIB/faceplate/CMD/button_message_input.xml";
      }

      semRelease("semCMD");//  进程间通讯，释放semCMD，用于mail.xml画面的DI,DO,AI,AO元件自动向下挪动。
      semRelease("sem_em_CMD"); // 进程间通讯，通知embeded module CMD相关的元素有几个被加载了。
    }

    if (CMD_STATE == "STATE")
    {

      if (dpElementType(dpe) == DPEL_BOOL)
      {
        stringfileName = "objects/LIB/faceplate/STATE/led_bool.xml";
      }
      else
      {
        stringfileName = "objects/LIB/faceplate/STATE/led_float_int.xml";
      }

      semRelease("semSTATE");// 进程间通讯，释放semCMD，用于mail.xml画面的DI,DO,AI,AO元件自动向下挪动。
      semRelease("sem_em_STATE"); // 进程间通讯，通知embeded module STATE 相关的元素有几个被加载了。
    }

    if (CMD_STATE == "ERROR")
    {
      stringfileName = "objects/LIB/faceplate/ERROR/alert_float.xml";
      semRelease("semERROR");// 进程间通讯，释放semERROR，用于mail.xml画面的DI,DO,AI,AO元件自动向下挪动。
      semRelease("sem_em_ERROR"); // 进程间通讯，通知embeded module STATE 相关的元素有几个被加载了。
    }


    string sId = (string)rand();
    string stringmoduleName = myModuleName(),
           stringpanelName = myPanelName(),
           stringrefName = "ref" + sId;


    dyn_string  dyn_stringsubst = makeDynString("$dpe:" + dpe) ;

    float floatangle = 0.0,
          floatscx = 1.0,
          floatscy = 1.0;

    addSymbol(shPanel,
              stringfileName,
              stringrefName,
              dyn_stringsubst,
              20, // 固定X坐标=20；
              g_posY,
              floatangle,
              floatscx,
              floatscy
             );

    g_posY += 22; //控制 STATE内部的item的间距
  }


  // 向前端页面发送完成信号，终止对应的等待的进程
  if (CMD_STATE == "STATE")
  {
    semRelease("sem_em_STATE_done");
    semRelease("sem_sizeOptimization_STATE_done");
  }

  if (CMD_STATE == "CMD")
  {
    semRelease("sem_em_CMD_done");
    semRelease("sem_sizeOptimization_CMD_done");
  }

  if (CMD_STATE == "ERROR")
  {
    semRelease("sem_em_ERROR_done");
    semRelease("sem_sizeOptimization_ERROR_done");
  }

  g_posY += 20; //控制 CMD,STATE,ERROR的间距。
}

// 设备点击弹窗后，加载CMD,STATE,ERROR等字段。
// YGap: g_posY += 8;
LIB_CMD_STATE_SHOW_DOUBLE(string CMD_STATE, int &Num, int &frequency)
{

  g_posY = 70 + 22 * Num + 25 * frequency;
  frequency++;
  string dpType =  dpTypeName(dp);

  // 获得 dpe 列表
  dyn_string dsDps, dsDpsNoValue;

  // 获得dpe的列表有两种方式：
  // 优先从 DPE.CMD.这个结构体的描述中获得（通过导入点位的时候进行赋值，是由Excel定义的顺序。）。
  // 若不存在，则从系统当前进行检索，按字母顺序排序。
  if (!dpExists(dp + "." + CMD_STATE))
  {

    if (shapeExists("txt" + CMD_STATE))
    {

      setValue("txt" + CMD_STATE, "visible", false);
    }

    return;
  }

  string sDescription = dpGetDescription(dp + "." + CMD_STATE);
  dyn_string dsConfig2 =  jsonDecode(sDescription);

  if (dynlen(dsConfig2) > 0)
  {
    for (int i = 1; i <= dynlen(dsConfig2); i++)
    {
      dsDps[i] = dp + "." + CMD_STATE + "." + dsConfig2[i] + ".VALUE" ; //
      dsDpsNoValue[i] = dp + "." + CMD_STATE + "." + dsConfig2[i]   ; //dsDpsNoValue是不含.VALUE的DPE, 用于读取dp的描述
    }
  }
  else
  {
    dsDps = dpNames(dp + "." + CMD_STATE + ".*.VALUE", dpType);//STATE
    dsDpsNoValue = dpNames(dp + "." + CMD_STATE + ".*", dpType);//STATE
  }

  if (dynlen(dsDps) == 0)
  {
    if (shapeExists("txt" + CMD_STATE))
    {
      setValue("txt" + CMD_STATE, "visible", false);
    }

    return;
  }



  if (shapeExists("txt" + CMD_STATE))
  {

    setValue("txt" + CMD_STATE, "position", 20, g_posY + 10);
  }

//    g_posY += 10;




  // 遍历DPE列表
  for (int i = 1; i <= dynlen(dsDps); i++)
  {
    string dpe = dsDps[i]  ; //
    string dpeNoValue = dsDpsNoValue[i];

    // DebugN(dpe);
    // 类型 DO AO
    if (i > g_Row_Num)
    {
      g_posX = 350;

      if (i == g_Row_Num + 1)
        g_posY = g_posY - 22 * g_Row_Num;
    }
    else
    {
      g_posX = 20;
    }

    // 检查地址是否存在？不存在则不显示
    if (!dpExists(dpe + ":_address.._type"))
    {
      continue;
    }

    int _type;
    dpGet(dpe + ":_address.._type", _type);

    if (_type == DPCONFIG_NONE)
    {
      continue;
    }

    string stringfileName;

    if (CMD_STATE == "CMD")
    {

      if (dpElementType(dpe) == DPEL_BOOL)
      {

        // 读取 dsDpsNoValue 的描述，PULSE_OPEN, PULSE_CLOSE
        if (dpGetDescription(dpeNoValue) == "PULSE_OPEN")
        {
          stringfileName = "objects/LIB/faceplate/CMD/button_pulse_open.xml";
        }
        else if (dpGetDescription(dpeNoValue) == "PULSE_CLOSE")
        {
          stringfileName = "objects/LIB/faceplate/CMD/button_pulse_close.xml";
        }
        else
        {
          stringfileName = "objects/LIB/faceplate/CMD/button_switch_2.xml";
        }
      }
      else
      {
        stringfileName = "objects/LIB/faceplate/CMD/button_message_input.xml";
      }
    }

    if (CMD_STATE == "STATE")
    {

      if (dpElementType(dpe) == DPEL_BOOL)
      {
        stringfileName = "objects/LIB/faceplate/STATE/led_bool.xml";
      }
      else
      {
        stringfileName = "objects/LIB/faceplate/STATE/led_float_int.xml";
      }
    }

    if (CMD_STATE == "ERROR")
    {

      if (dpElementType(dpe) == DPEL_BOOL)
      {
//         stringfileName = "objects/LIB/faceplate/ERROR/alert_bool.xml";
//       }
//       else
//       {
        stringfileName = "objects/LIB/faceplate/ERROR/alert_float.xml";
      }
    }


    string sId = (string)rand();
    string stringmoduleName = myModuleName(),
           stringpanelName = myPanelName(),
           stringrefName = "ref" + sId;


    dyn_string  dyn_stringsubst = makeDynString("$dpe:" + dpe) ;

    float floatangle = 0.0,
          floatscx = 1.0,
          floatscy = 1.0;

    /*
        addSymbol(stringmoduleName,
                  stringpanelName,
                  stringfileName,
                  stringrefName,
                  dyn_stringsubst,
                  20, // 固定X坐标=20；
                  g_posY,
                  floatangle,
                  floatscx,
                  floatscy
                 );
    */

    addSymbol(shPanel,
              //stringmoduleName,
              //stringpanelName,
              stringfileName,
              stringrefName,
              dyn_stringsubst,
              g_posX, // 固定X坐标=20；
              g_posY + 12,
              floatangle,
              floatscx,
              floatscy
             );

    g_posY += 22; //控制 STATE内部的item的间距

    if (i <= g_Row_Num)
    {
      Num++;
    }
  }


  g_posY += 20; //控制 CMD,STATE,ERROR的间距。

}

LIB_UpdateHornConfig()
{

  if (!globalExists("g_mHornConfig"))
  {
    addGlobal("g_mHornConfig", MAPPING_VAR);
  }

  if (isfile(getPath(CONFIG_REL_PATH + "LIB_HornConfig.json")))
  {
    string str;
    fileToString(getPath(CONFIG_REL_PATH + "LIB_HornConfig.json"), str);
    g_mHornConfig = jsonDecode(str);

    if (_UNIX) g_mHornConfig["SoundFilePath"] = "";

    if (_UNIX) g_mHornConfig["CortanaEnable"] = FALSE;

    if (_UNIX) g_mHornConfig["Mute"] = TRUE;
  }
  else
  {
    // 初始化配置文件
    g_mHornConfig["AlertPriorityEnabled"] = true;
    g_mHornConfig["AlertPriorityLimit"] = 40;
    g_mHornConfig["CortanaEnable"] = TRUE;
    g_mHornConfig["Mute"] = FALSE;
    g_mHornConfig["MuteDelayTime"] = 10;
    g_mHornConfig["PopupWindow_autoPanelOff"] = TRUE;
    g_mHornConfig["PopupWindow_autoPanelOffDelayTime"] = 31;
    g_mHornConfig["PopupWindow_enable"] = TRUE;
    g_mHornConfig["SoundFilePath"] =  getPath(DATA_REL_PATH,  "ding.wav");
    g_mHornConfig["UISingleAck"] = FALSE;
    g_mHornConfig["WhereToPlay"] = 1;
    g_mHornConfig["loopSound"] = TRUE;
    g_mHornConfig["txtPrefix"] = "洁净系统";


    if (_UNIX) g_mHornConfig["SoundFilePath"] = "";

    if (_UNIX) g_mHornConfig["CortanaEnable"] = FALSE;

    if (_UNIX) g_mHornConfig["Mute"] = TRUE;

    string json ;
    file f = fopen(getPath(CONFIG_REL_PATH + "LIB_HornConfig.json"), "w+");
    fputs(jsonEncode(g_mHornConfig, false), f);
    fflush(f);
    fclose(f);
  }

}

// LIB库
// 处理与报警条有关的报警弹窗、报警发声、Cortana等、多UI的确认。
void LIB_AlertHorn()
{
  LIB_UpdateHornConfig();

  delay(2); // 一开始的时候，不要启动。
  startThread("LIB_AlertHorn_thread");
  startThread("LIB_AlertSingleAckByUI_thread");
}

/**
  LIB_AlertHorn_thread.
  以1s周期统计表格行数 table_top.lineCount
  若有增加，显示报警弹窗，播放声音，
  @param none
  @author zhigang-li@siemens.com
  @version 1.0
  @return nothing
*/
LIB_AlertSingleAckByUI_thread()
{
  while (1)
  {
    // 遍历报警表格
    int iColumnCount;
    getValue("table_top", "columnCount", iColumnCount);
    dyn_string dsHeaderName;

    for (int i = 0; i < iColumnCount ; i++)
    {
      dynAppend(dsHeaderName,  table_top.columnToName(i));
    }

    /*
    DebugN(dsHeaderName);
    WCCOAui2:     1: "abbreviation"
    WCCOAui2:     2: "priority"
    WCCOAui2:     3: "timeStr"
    WCCOAui2:     4: "elementName"
    WCCOAui2:     5: "alertText"
    WCCOAui2:     6: "direction"
    WCCOAui2:     7: "value"
    WCCOAui2:     8: "acknowledge"
    WCCOAui2:     9: "ackTime"
    WCCOAui2:    10: "nofComments"
    WCCOAui2:    11: "alertPanel"
    WCCOAui2:    12: "detail"
    WCCOAui2:    13: "__V_time"
    WCCOAui2:    14: "__V_sysName"
    WCCOAui2:    15: "__V_ackable"
    WCCOAui2:    16: "__V_comments"
    WCCOAui2:    17: "__V_oldestAck"
    WCCOAui2:    18: "__V_ackState"
    WCCOAui2:    19: "__V_dpid"
    WCCOAui2:    20: "__V_count"
    WCCOAui2:    21: "__alertClass"
    */

    delay(1);
  }
}

/**
  LIB_AlertHorn_thread.
  以1s周期统计表格行数 table_top.lineCount
  若有增加，显示报警弹窗，播放声音，
  @param none
  @author zhigang-li@siemens.com
  @version 1.0
  @return nothing
*/
LIB_AlertHorn_thread()
{


  int lineCount0, lineCount1;    // 获得表格的行数

  while (1)
  {
    lineCount0 = table_top.lineCount();
    delay(1);
    lineCount1 = table_top.lineCount();

    if (lineCount1 > lineCount0) // 行数增加，=》新增加报警。
    {

      LIB_UpdateHornConfig();

      // 处理新增加的报警
      int alertTextRowColumnNumber;
      int alertLevelRowColumnNumber;
      int WhereToPlay = g_mHornConfig["WhereToPlay"];
      string alertText;
      int MuteDelayTime = g_mHornConfig["MuteDelayTime"];

      string abbreviation;
      string txtPrefix;
      bool Mute = g_mHornConfig["Mute"];
      bool AlertPriorityEnabled = g_mHornConfig["AlertPriorityEnabled"];

      string dp;
      string dpe;

      // 静音
      if (Mute)
      {
        continue;
      }

      // 显示的位置=0.
      if (WhereToPlay == 0)
      {
        continue;
      }

      // 遍历表格
      int iColumnCount;
      getValue("table_top", "columnCount", iColumnCount);
      dyn_string dsHeaderName;

      for (int i = 0; i < iColumnCount ; i++)
      {
        dynAppend(dsHeaderName,  table_top.columnToName(i));
      }

      /*
      DebugN(dsHeaderName);
      WCCOAui2:     1: "abbreviation"
      WCCOAui2:     2: "priority"
      WCCOAui2:     3: "timeStr"
      WCCOAui2:     4: "elementName"
      WCCOAui2:     5: "alertText"
      WCCOAui2:     6: "direction"
      WCCOAui2:     7: "value"
      WCCOAui2:     8: "acknowledge"
      WCCOAui2:     9: "ackTime"
      WCCOAui2:    10: "nofComments"
      WCCOAui2:    11: "alertPanel"
      WCCOAui2:    12: "detail"
      WCCOAui2:    13: "__V_time"
      WCCOAui2:    14: "__V_sysName"
      WCCOAui2:    15: "__V_ackable"
      WCCOAui2:    16: "__V_comments"
      WCCOAui2:    17: "__V_oldestAck"
      WCCOAui2:    18: "__V_ackState"
      WCCOAui2:    19: "__V_dpid"
      WCCOAui2:    20: "__V_count"
      WCCOAui2:    21: "__alertClass"
      */
      if (WhereToPlay == 1) // 1= 仅报警条允许
      {

        // 判断当前是不是报警条 infoModule_1
        // DebugN( myPanelName(), myModuleName()); // ["Info"]["infoModule_1"]
        string panelName, moduleName;
        panelName = myPanelName();
        moduleName =  myModuleName();

        if (strpos(moduleName, "infoModule") == -1)
        {
          continue;
        }
      }
      else if (WhereToPlay == 2) // 2= 仅报警画面允许
      {
        // 判断当前是不是报警画面 mainModule_1
        string panelName, moduleName;
        panelName = myPanelName();
        moduleName =  myModuleName();

        if (strpos(moduleName, "mainModule") == -1)
        {
          continue;
        }
      }
      else if (WhereToPlay == 3) // 3= 后台 尚未编写。
      {
        continue;
      }
      else // 其他地方，尚未编写。
      {
        continue;
      }

      // 报警方向
      if (dynContains(dsHeaderName, "direction"))
      {
        bool direction;
        getValue("table_top", "cellValueRC", lineCount1 - 1, "direction", direction);

        if (direction == false) // went
        {
          continue;
        }
      }

      // dp, dpe
      if (dynContains(dsHeaderName, "__V_dpid"))
      {
        getValue("table_top", "cellValueRC", lineCount1 - 1, "__V_dpid", dpe);
        dp = dpSubStr(dpe, DPSUB_SYS_DP);
      }
      else
      {
        DebugN(__FUNCTION__, __FILE__, "不得删除__V_dpid这一隐藏的列，退出！");
        return;
      }

      // 报警级别
      if (AlertPriorityEnabled)
      {

        int priority;

        if (dynContains(dsHeaderName, "priority"))
        {

          getValue("table_top", "cellValueRC", lineCount1 - 1, "priority", priority);
        }
        else
        {
          priority = 10;
        }

        if (priority < g_mHornConfig["AlertPriorityLimit"])
        {
          continue;
        }
      }

      // 报警等级文本 一级报警、二级报警、

      if (dynContains(dsHeaderName, "abbreviation"))
      {
        getValue("table_top", "cellValueRC", lineCount1 - 1, "abbreviation", abbreviation);
      }

      string elementName;

      if (dynContains(dsHeaderName, "elementName"))
      {
        getValue("table_top", "cellValueRC", lineCount1 - 1, "elementName", elementName);
      }

      string alertText;

      if (dynContains(dsHeaderName, "alertText"))
      {
        getValue("table_top", "cellValueRC", lineCount1 - 1, "alertText", alertText);
      }

      // 报警弹窗
      // 条件：弹窗使能 、 G_PLAYING_SOUND全局变量==“”；禁止多条报警触发多条语音，只触发第一条；目的：实现吸引操作员前来查看的目的即可。
      if (g_mHornConfig["PopupWindow_enable"] && (G_PLAYING_SOUND == ""))
      {
        // DebugN(abbreviation, alertText); //二级报警  F1_CW_1_HWP_1_PUMP GAS Leak PreHI
        // 弹窗
        ChildPanelOnRelativModal("tools/LIB_PopupWindowAlert.xml", "popwindow",
                                 makeDynString("$dpe:" + dpe,
                                               "$abbreviation:" + abbreviation,
                                               "$elementName:" + elementName,
                                               "$alertText:"  + alertText
                                              ), 0, 0);
      }

      if (g_mHornConfig["CortanaEnable"] && (G_PLAYING_SOUND == ""))
      {
        // 后台循环播放声音..
        startThread("LIB_PlaySound", dpe,  abbreviation);
      }


    }
  }
}

LIB_PlaySound(string dpe,  string abbreviation)
{


  if (!globalExists("G_PLAYING_SOUND"))
  {
    addGlobal("G_PLAYING_SOUND", STRING_VAR);
  }

  bool loopSound = g_mHornConfig["loopSound"];

  string txtPrefix = g_mHornConfig["txtPrefix"];

  // mp3、wav文件; fix 无限循环播报声音
  if (loopSound)
  {

    G_PLAYING_SOUND = dpe + "</br>"  + txtPrefix + "</br>" + abbreviation; // 等待其他界面的按钮将此数值写false，以关闭此次声音播放
  }
  else
  {
    G_PLAYING_SOUND = "";
  }



  // 首次执行一次。 循环播放
  do
  {
    loopSound = g_mHornConfig["loopSound"];

    string SoundFilePath = g_mHornConfig["SoundFilePath"];

    if (isfile(SoundFilePath))
    {
      startSound(SoundFilePath);
    }

    delay(1);

    // cortana语音
    bool CortanaEnable = g_mHornConfig["CortanaEnable"];


    if (CortanaEnable && _WIN32) // only for win32
    {
      // 文件存在
      if (isfile(getPath(BIN_REL_PATH, "tts_vc.exe")))
      {
        txtPrefix = g_mHornConfig["txtPrefix"];

        if (_WIN32)
        {
          string sysName = dpSubStr(dpe, DPSUB_SYS); // 获得系统名称。
          strreplace(sysName, ":", "");

          if (sysName == "System1")
          {
            sysName = "";
          }

//           system("start /b " + getPath(BIN_REL_PATH, "tts_vc.exe") + " "  +  txtPrefix + sysName + "系统" + abbreviation);
          system("start /b " + getPath(BIN_REL_PATH, "tts_vc.exe") + " "  +  txtPrefix + sysName +  abbreviation);
        }
      }
      else // 不存在，拷贝再播放
      {
        DebugN("bin/tts_vc.exe" + "在所有的路径下都不存在，无法使用Cortana！");
      }


    }

    delay(strlen(txtPrefix + abbreviation) / 2);     // 1s 一个字的速度

  }
  while (loopSound & (G_PLAYING_SOUND != ""));

  // 报警延时静音
  // startThread("LIB_AlertHorn_AlertRow_mute");

}

// 弃用了。
LIB_AlertHorn_AlertRow_mute()
{
  LIB_UpdateHornConfig();
  int MuteDelayTime =  g_mHornConfig["MuteDelayTime"] ;

  if (MuteDelayTime > 0)
  {
    g_mHornConfig["Mute"] = true;
    delay(MuteDelayTime);
    g_mHornConfig["Mute"] = false;
  }
}


LIB_CreateSumalertWithPattern(string dp)
{

  int _type;
  bool _active;

  if (dpExists(dp + ".ERROR.SUM_ALERT:_alert_hdl.._type"))
  {

    dpGet(dp + ".ERROR.SUM_ALERT:_alert_hdl.._type", _type,
          dp + ".ERROR.SUM_ALERT:_alert_hdl.._active", _active);

    if (_active)
    {

      dpSetTimed(0, dp + ".ERROR.SUM_ALERT:_alert_hdl.._ack", 2);

      dpSetTimed(0, dp + ".ERROR.SUM_ALERT:_alert_hdl.._active", false);

    }

    dpSetTimed(0, dp + ".ERROR.SUM_ALERT:_alert_hdl.._type", DPCONFIG_SUM_ALERT,
               dp + ".ERROR.SUM_ALERT:_alert_hdl.._dp_pattern", dp + ".ERROR.*.VALUE",
               dp + ".ERROR.SUM_ALERT:_alert_hdl.._active", true);

  }


}

// 导入地址工具-MODBUS
// 弃用!仅保留兼容性!勿删~
LIB_CreateDPEByImportTool_Modbus(string dpType,
                                 string path,
                                 string PLC,
                                 string PLC_Driver_Number,
                                 bool reset = false,
                                 string NGA_OR_ValueArchive = "NGA"
                                )
{
  LIB_CreateDPEByImportTool("MODBUS",   dpType,   path,   PLC,   PLC_Driver_Number, reset, NGA_OR_ValueArchive);
}

//  导入地址工具-S7
// 弃用!仅保留兼容性!勿删~
LIB_CreateDPEByImportTool_S7(string dpType,
                             string path,
                             string PLC,
                             string PLC_Driver_Number,
                             bool reset = false,
                             string NGA_OR_ValueArchive = "NGA")
{
  LIB_CreateDPEByImportTool("S7",   dpType,   path,   PLC,   PLC_Driver_Number, reset, NGA_OR_ValueArchive);
}

// NGA_OR_ValueArchive NGA 或 ValueArchive
// driverType: "S7", "MODBUS", "OPC"
// path: 使用函数 getPath( DATA_REL_PATH ) + "dp/DUMMY/F1_CUB_1_DUMMY_PLC01/06_AI.txt";获得的DP配置路径。
// PLC:驱动的PLC DP点特征。S7驱动应为_xxx,_xxx_2,; modbus :  _Mod_Plc_x, _Mod_Plc_x_2
// PLC_Driver_Number：-num x
// reset 1:覆盖 0：终止
// NGA_OR_ValueArchive "NGA", "ValueArchive"
LIB_CreateDPEByImportTool(string driverType,
                          string dpType,
                          string path,
                          string PLC,
                          int PLC_Driver_Number,
                          bool reset = false,
                          string NGA_OR_ValueArchive = "NGA"
                         )
{

  if (!dpExists("_mp_" + dpType))
  {
    mpCreateMpDp(dpType, 0, FALSE);
    delay(0, 10);
  }

  if (!isfile(path))
  {
    DebugN(__FUNCTION__, "路径错误:" + path);
    return;
  }

  string str;
  fileToString(path, str);

  if (str == "")
  {
    DebugN(__FUNCTION__, "文件内容为空:" + path);
    return;
  }

  dyn_string rows = strsplit(str, "\n");

  int count, error;

  string dp, dpe;

  // 表头第1行：描述
  dyn_string dsDescription = strsplit(rows[1], "\t");

  // 表头第2行：config1 。
  dyn_string dsConfig1 = strsplit(rows[2], "\t");

  // 表头第3行：config2 。
  dyn_string dsConfig2 = strsplit(rows[3], "\t");

  // 表头第4行：数据类型
  dyn_string dsDataType = strsplit(rows[4], "\t");

  // 表头第5行：报警等级
  dyn_string dsPriority = strsplit(rows[5], "\t");

  // 表头第6行：操作权限
  dyn_string dsAuthorization = strsplit(rows[6], "\t");

  dyn_string dsCMDSort, dsSTATESort, dsERRORSort; // 用于读取Excel的CMD,STATE,ERROR先后顺序，在弹窗中按Excel进行排序显示。

  // 从第7行开始 检查是不是存在，进行警告！
  for (int i = 7; i <= dynlen(rows); i++)
  {

    dyn_string dsColumns = strsplit(rows[i], "\t");

    dp =   dsColumns[1];

    if (dpExists(dp))
    {
      if (!reset)
      {
        DebugN("已经存在该DP->" + dp  + " Type : " + dpType + ", 未新建! 无法继续，请检查！请务必断开PLC连接进行操作！！！退出！");
        return; //
      }
      else
      {
        DebugN("已经存在该DP->" + dp  + " Type : " + dpType + ", 将会覆盖现有参数！");
      }
    }
  }

  // 从第7行开始 地址配置的正文。
  for (int i = 7; i <= dynlen(rows); i++)
  {
    // DP  如果不存在,建立DP.
    dyn_string dsColumns = strsplit(rows[i], "\t");

    // 第1列，DP NAME
    dp =   dsColumns[1];

    // 第2列，设备描述
    string sDeviceDescription = dsColumns[2];

    DebugN("");
    DebugN(i  + "/" + dynlen(rows) + "/" + dp + "/" + sDeviceDescription);

    // 第3列，单位
    string sUnit = dsColumns[3];

    // 第4列，格式与精度
    string sFormat = (dsColumns[4] != "") ? dsColumns[4] : "%4.2f";

    // 第5列，量程-L
    float fSpanLow = minFLOAT();
    float fSpanHigh = maxFLOAT();

    if (dsColumns[5] != "")
    {

      fSpanLow = dsColumns[5];
    }
    else
    {
      fSpanLow = 0; //
    }

    // 第6列，量程-H
    if (dsColumns[6] != "")
    {

      fSpanHigh = dsColumns[6];
    }
    else
    {
      fSpanHigh = 100.0; // 默认100.
    }


    if (!dpExists(dp))
    {

      //dpCreate(dp, "LIB_GMS_DET_T");
      dpCopyBufferClear(); // call this before dpCopy().
      dpCopy("_mp_" + dpType, dp, error, PLC_Driver_Number);
      delay(0, 20);

      if (error == 0)
      {
        DebugN("New DP->" + dp  + " Type :  " + dpType + ", OK!");
      }
      else
      {
        DebugN("FATAL: DP->" + dp  + " Type :  " + dpType + ", ERROR!", error, showDpCopyError(error));
        return;
      }
    }

    // PLC 是否激活？激活的PLC进行地址设定是非常危险！要防范写PLC数据到设备执行。！！！

    string PLCDPName;

    switch (driverType)
    {
      case "S7":

        // 未建立S7连接！
        if (!dpExists("_" + PLC))
        {
          DebugN("请先建立S7 连接！！！ " + PLC);
          return;
        }

        // 未发现_2的驱动点位！自动新建
        if (!dpExists("_" + PLC + "_2"))
        {
          int error;
          dpCopy("_" + PLC,  "_" + PLC + "_2", error);
          delay(0, 10);
          DebugN("自动创建了备机点位：" + PLC + "_2");
        }

        break;

      case "MODBUS":

        // 未建立MODBUS连接！
        if (!dpExists("_Mod_Plc_" + PLC))
        {
          DebugN("【错误！终止！】请先建立Modbus 连接,ID:" + PLC);
          return;
        }

        // 未发现_2的驱动点位！自动新建
        if (!dpExists("_Mod_Plc_" + PLC + "_2"))
        {
          int error;
          dpCopy("_Mod_Plc_" + PLC,  "_Mod_Plc_" + PLC + "_2", error);
          delay(0, 10);
          DebugN("自动创建了 modbus 备机点位, _Mod_Plc_" + PLC + "_2");
        }

        break;

      case "OPC":
        //预留OPC 接口
        break;

    }

    // 第7列开始，地址配置。最后一列是占位。
    dynClear(dsSTATESort);
    dynClear(dsCMDSort);
    dynClear(dsERRORSort);

    for (int column = 7; column < dynlen(dsDescription)  ; column++)  // 地址列：7~(n-1)
    {
      string dpe = dp + "." + dsConfig1[column] + "." + dsConfig2[column] + ".VALUE";

      // 1. 地址
      switch (driverType)
      {
        case "S7":
          LIB_SetS7Address(dpe, dsColumns[column], PLC, PLC_Driver_Number, "_siemens_polling_1s");
          break;

        case "MODBUS":
          LIB_SetModbusAddress(dpe, dsColumns[column], PLC_Driver_Number, "_modbus_polling_1s");
          break;

        case "OPC":
          // 预留OPC驱动设置接口！
          break;
      }

      // 2. 设备描述
      string dpeDescription = dsDescription[column];
      dpSetDescription(dp + ".", sDeviceDescription);
      dpSetDescription(dpe, sDeviceDescription + "." + dsDescription[column]);

      // 3. unit物理单位- 仅针对 AI, AO, WORD , DWORD
      if ((sUnit != "")
          && (dsDataType[column] != "DI")
          && (dsDataType[column] != "DO")
          && (dsDataType[column] != "PULSE_OPEN")
          && (dsDataType[column] != "PULSE_CLOSE")
         )
      {
        dpSetUnit(dpe,  sUnit);
      }

      // 4. 数据格式
      if ((dsDataType[column] == "AI") || (dsDataType[column] == "AO"))
      {
        dpSetFormat(dpe, sFormat);
      }

      // 5. 报警信息
      if ((dsConfig1[column] == "ERROR") && (dsDataType[column]) == "DI")     // 仅针对BOOL量。
      {
        string alertClass ;

        switch (dsPriority[column])  // 读出报警列
        {
          case "一级报警":
            alertClass = "LIB_Alert_Level1.";
            break;

          case "二级报警":
            alertClass = "LIB_Alert_Level2.";
            break;

          case "三级报警":
            alertClass = "LIB_Alert_Level3.";
            break;

          case "紧急报警":
            alertClass = "LIB_Critical.";
            break;

          case "报警隔离":
            alertClass = "LIB_Alert_Level0.";
            break;

          default:
            alertClass = "LIB_Alert_Level3.";
        }

        LIB_SetBoolAlert(dpe, alertClass,   dsDescription[column]);

      }

      // 7.  auth 权限位 （仅对CMD的控制点位 )
      if (dsConfig1[column] == "CMD")
      {
        if (dsAuthorization[column] != "")
        {
          LIB_SetDPEWriteAuth(dpe, (int)dsAuthorization[column]);
        }
        else
        {
          LIB_SetDPEWriteAuth(dpe, 6);
        }
      }

      // 8. MIN, MAX。 此处应当指的是仪表的量程。
      if ((fSpanLow > minFLOAT()) || (fSpanHigh < maxFLOAT()))
      {
        dpSetWait(dpe + ":_pv_range.._type", DPCONFIG_MINMAX_PVSS_RANGECHECK,
                  dpe + ":_pv_range.._min", fSpanLow,
                  dpe + ":_pv_range.._max", fSpanHigh,
                  dpe + ":_pv_range.._incl_min", true,
                  dpe + ":_pv_range.._incl_max", true,
                  dpe + ":_pv_range.._ignor_inv", false,
                  dpe + ":_pv_range.._neg", false);
      }

      // 9. archive

      if (NGA_OR_ValueArchive == "ValueArchive")  // WinCC OA version < 3.19
      {
        // 地址不为空，则可以进行归档。
        if (dsColumns[column] != "")
        {
          LIB_ValueArchive(dpe);
        }
      }

      // 10. 允许空地址（占位和备用的作用），若地址不存在，取消归档的配置。
      if ((dsColumns[column] == "") || (dsColumns[column] == "NULL"))
      {
        dpSetWait(dpe + ":_archive.._type", DPCONFIG_NONE);
      }

      // 11. CMD,STATE,ERROR 分别按Excel的先后顺序进行排序，存储到DP.CMD. DP.STATE, DP.ERROR. 的结构体的中文描述（利用一下中文描述存储）。
      // 分别收集排序信息到dsCMDSort, dsSTATESort,dsERRORSort，用于进行弹出面板的排序的显示。

      if (dsConfig1[column] == "CMD")
      {
        dynAppend(dsCMDSort, dsConfig2[column]);
      }

      if (dsConfig1[column] == "STATE")
      {
        dynAppend(dsSTATESort, dsConfig2[column]);
      }

      if (dsConfig1[column] == "ERROR")
      {
        dynAppend(dsERRORSort, dsConfig2[column]);
      }

      // 12. 如果包含PULSE_OPEN 脉冲开, PULSE_CLOSE 脉冲关, 将PULSE_OPEN/ PULSE_CLOSE写入到DPE的描述中。DPE是不含.VALUE的DPE。
      // 如：UPW_CUB_AV_402a.CMD.HMI_ON.VALUE，写入到UPW_CUB_AV_402a.CMD.HMI_ON的结构体类型的描述中。
      if (dsDataType[column] == "PULSE_OPEN")
      {
        dpSetDescription(dp + ".CMD." + dsConfig2[column], "PULSE_OPEN");
      }

      if (dsDataType[column] == "PULSE_CLOSE")
      {
        dpSetDescription(dp + ".CMD." + dsConfig2[column], "PULSE_CLOSE");
      }
    }

    // 处理11所描述的CMD,STATE,ERROR的排序记录到结构体的描述字段。
    if (dynlen(dsCMDSort) > 0)
    {

      dpSetDescription(dp + ".CMD", jsonEncode(dsCMDSort)) ;
    }

    if (dynlen(dsSTATESort) > 0)
    {

      dpSetDescription(dp + ".STATE", jsonEncode(dsSTATESort)) ;
    }

    if (dynlen(dsERRORSort) > 0)
    {

      dpSetDescription(dp + ".ERROR", jsonEncode(dsERRORSort)) ;
    }

    // 恢复以前的PLC的设定
    // dpSet("_" + PLC +  ".Active", Active1);
    // dpSet("_" + PLC +  "_2.Active", Active2);

    // 自动生成 SUM ALERT
    string sysName = getSystemName();
    LIB_CreateSumalertWithPattern(sysName + dp);

    // 修改GENERAL.NOTE的描述=”操作日志“,对于一些不需要CMD的点位，就不需要设置GENERAL.NOTE。
    if (dpExists(dp + ".GENERAL.NOTE"))
    {
      dpSetDescription(dp + ".GENERAL.NOTE", "操作日志") ;
    }
  }

  LIB_mpDeleteMpDp("_mp_" + dpType);
////
  DebugN("path:" + path);
  DebugN("dpType:" + dpType);
  DebugN("PLC:" + PLC);
  DebugN("PLC_Driver_Number : " + PLC_Driver_Number);
  DebugN("执行完毕,OK");
}

// 设置value archive
// STATE 下的模拟量输入，都归入到AI FAST ；如果有配套的HOUR ,MINUTE 点位，归入到对应的HOUR MINUTE 下。
// 其他的，CMD, ERROR等，都归入到DI-DO-AO
LIB_ValueArchive(string dpe)
{
  // 是STATE 且 AI点位
  if (strpos(dpe, "STATE") > 0 && dpElementType(dpe) == DPEL_FLOAT)
  {
    dpSetWait(dpe + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO,
              dpe + ":_archive.._archive", false);

    dpSetWait(dpe + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO, // 45
              dpe + ":_archive.._archive", true,
              dpe + ":_archive.1._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
              dpe + ":_archive.1._class", "_ValueArchive_2",
              dpe + ":_archive.1._std_type", DPATTR_TIME_OR_VALUE_REL_SMOOTH, // 9,
              dpe + ":_archive.1._std_tol", 0.2,
              dpe + ":_archive.1._std_time", 0
             );
    // 对于带有附属点位的HOUR, MINUTE点位
    string dpeHour, dpeMinute;
    dpeHour = dpe;
    dpeMinute = dpe;
    strreplace(dpeHour, ".VALUE", ".HOUR.VALUE");
    strreplace(dpeMinute, ".VALUE", ".MINUTE.VALUE");

    if (dpExists(dpeMinute))
    {
      dpSetWait(dpeMinute + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO,
                dpeMinute + ":_archive.._archive", false);

      dpSetWait(dpeMinute + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO, // 45
                dpeMinute + ":_archive.._archive", true,
                dpeMinute + ":_archive.1._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
                dpeMinute + ":_archive.1._class", "_ValueArchive_5",
                dpeMinute + ":_archive.1._std_type", DPATTR_VALUE_SMOOTH, // 9,
                dpeMinute + ":_archive.1._std_tol", 0,
                dpeMinute + ":_archive.1._std_time", 0
               );
    }

    if (dpExists(dpeHour))
    {
      dpSetWait(dpeHour + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO,
                dpeHour + ":_archive.._archive", false);

      dpSetWait(dpeHour + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO, // 45
                dpeHour + ":_archive.._archive", true,
                dpeHour + ":_archive.1._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
                dpeHour + ":_archive.1._class", "_ValueArchive_8",
                dpeHour + ":_archive.1._std_type", DPATTR_VALUE_SMOOTH, // 9,
                dpeHour + ":_archive.1._std_tol", 0,
                dpeHour + ":_archive.1._std_time", 0
               );
    }
  }
  else
  {
    dpSetWait(dpe + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO,
              dpe + ":_archive.._archive", false);

    dpSetWait(dpe + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO, // 45
              dpe + ":_archive.._archive", true,
              dpe + ":_archive.1._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
              dpe + ":_archive.1._class", "_ValueArchive_11",
              dpe + ":_archive.1._std_type", DPATTR_COMPARE_OLD_NEW, // 4,
              dpe + ":_archive.1._std_tol", 0.2,
              dpe + ":_archive.1._std_time", 0
             );
  }


}


// 设置NGA归档，同时支持2路（本地、远程）
// subSystemId : CR, MEP....
// 归档命名规范：
// 本地：MEP_AI_FAST
// 远程：HIS_MEP_AI_FAST
LIB_NGAArchive2(string dpe)
{
  string subSystemId = dpSubStr(dpe, DPSUB_SYS);
  strreplace(subSystemId, ":", "");

  // 是STATE 且 AI点位
  if (dpElementType(dpe) == DPEL_FLOAT)
  {
    dpSetWait(dpe + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO,
              dpe + ":_archive.._archive", false);

    dpSetWait(dpe + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO, // 45
              dpe + ":_archive.._archive", true,
              dpe + ":_archive.1._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
              dpe + ":_archive.1._class", "_NGA_G_" + subSystemId + "_AI_FAST", // _NGA_G_MEP_AI_FAST
              dpe + ":_archive.1._std_type", DPATTR_VALUE_REL_SMOOTH, // 9,Smoothing with relative deadband values
              dpe + ":_archive.1._std_tol", 0.2,
              dpe + ":_archive.1._std_time", 0,
              dpe + ":_archive.1._interv", 0,
              dpe + ":_archive.1._round_val", 0,
              dpe + ":_archive.1._round_inv", 0,

              dpe + ":_archive.2._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
              dpe + ":_archive.2._class", "_NGA_G_HIS_" + subSystemId + "_AI_FAST", // _NGA_G_HIS_CR_AI_FAST
              dpe + ":_archive.2._std_type", DPATTR_VALUE_REL_SMOOTH, // 9,Smoothing with relative deadband values
              dpe + ":_archive.2._std_tol", 0.2,
              dpe + ":_archive.2._std_time", 0,
              dpe + ":_archive.2._interv", 0,
              dpe + ":_archive.2._round_val", 0,
              dpe + ":_archive.2._round_inv", 0
             );
    // 对于带有附属点位的HOUR, MINUTE点位
    string dpeHour, dpeMinute;
    dpeHour = dpe;
    dpeMinute = dpe;
    strreplace(dpeHour, ".VALUE", ".HOUR.VALUE");
    strreplace(dpeMinute, ".VALUE", ".MINUTE.VALUE");

    if (dpExists(dpeMinute))
    {
      dpSetWait(dpeMinute + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO,
                dpeMinute + ":_archive.._archive", false);

      dpSetWait(dpeMinute + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO, // 45
                dpeMinute + ":_archive.._archive", true,
                dpeMinute + ":_archive.1._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
                dpeMinute + ":_archive.1._class", "_NGA_G_" + subSystemId + "_AI_SLOW", // _NGA_G_MEP_AI_FAST
                dpeMinute + ":_archive.1._std_type", DPATTR_VALUE_REL_SMOOTH, // 9,Smoothing with relative deadband values
                dpeMinute + ":_archive.1._std_tol", 0.2,
                dpeMinute + ":_archive.1._std_time", 0,
                dpeMinute + ":_archive.1._interv", 0,
                dpeMinute + ":_archive.1._round_val", 0,
                dpeMinute + ":_archive.1._round_inv", 0,

                dpeMinute + ":_archive.2._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
                dpeMinute + ":_archive.2._class", "_NGA_G_HIS_" + subSystemId + "_AI_SLOW", // _NGA_G_HIS_CR_AI_FAST
                dpeMinute + ":_archive.2._std_type", DPATTR_VALUE_REL_SMOOTH, // 9,Smoothing with relative deadband values
                dpeMinute + ":_archive.2._std_tol", 0.2,
                dpeMinute + ":_archive.2._std_time", 0,
                dpeMinute + ":_archive.2._interv", 0,
                dpeMinute + ":_archive.2._round_val", 0,
                dpeMinute + ":_archive.2._round_inv", 0
               );
    }

    if (dpExists(dpeHour))
    {
      dpSetWait(dpeHour + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO,
                dpeHour + ":_archive.._archive", false);

      dpSetWait(dpeHour + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO, // 45
                dpeHour + ":_archive.._archive", true,
                dpeHour + ":_archive.1._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
                dpeHour + ":_archive.1._class", "_NGA_G_" + subSystemId + "_AI_SLOW", // _NGA_G_MEP_AI_FAST
                dpeHour + ":_archive.1._std_type", DPATTR_VALUE_REL_SMOOTH, // 9,Smoothing with relative deadband values
                dpeHour + ":_archive.1._std_tol", 0.2,
                dpeHour + ":_archive.1._std_time", 0,
                dpeHour + ":_archive.1._interv", 0,
                dpeHour + ":_archive.1._round_val", 0,
                dpeHour + ":_archive.1._round_inv", 0,

                dpeHour + ":_archive.2._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
                dpeHour + ":_archive.2._class", "_NGA_G_HIS_" + subSystemId + "_AI_SLOW", // _NGA_G_HIS_CR_AI_FAST
                dpeHour + ":_archive.2._std_type", DPATTR_VALUE_REL_SMOOTH, // 9,Smoothing with relative deadband values
                dpeHour + ":_archive.2._std_tol", 0.2,
                dpeHour + ":_archive.2._std_time", 0,
                dpeHour + ":_archive.2._interv", 0,
                dpeHour + ":_archive.2._round_val", 0,
                dpeHour + ":_archive.2._round_inv", 0
               );
    }
  }
  else
  {
    dpSetWait(dpe + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO,
              dpe + ":_archive.._archive", false);

    dpSetWait(dpe + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO, // 45
              dpe + ":_archive.._archive", true,
              dpe + ":_archive.1._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
              dpe + ":_archive.1._class", "_NGA_G_" + subSystemId + "_DI_DO_AO", // MEP_DI_DO_AO
              dpe + ":_archive.1._std_type", DPATTR_VALUE_REL_SMOOTH, // 9,Smoothing with relative deadband values
              dpe + ":_archive.1._std_tol", 0.2,
              dpe + ":_archive.1._std_time", 0,
              dpe + ":_archive.1._interv", 0,
              dpe + ":_archive.1._round_val", 0,
              dpe + ":_archive.1._round_inv", 0,

              dpe + ":_archive.2._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
              dpe + ":_archive.2._class", "_NGA_G_HIS_" + subSystemId + "_DI_DO_AO", // _NGA_G_HIS_CR_AI_FAST
              dpe + ":_archive.2._std_type", DPATTR_VALUE_REL_SMOOTH, // 9,Smoothing with relative deadband values
              dpe + ":_archive.2._std_tol", 0.2,
              dpe + ":_archive.2._std_time", 0,
              dpe + ":_archive.2._interv", 0,
              dpe + ":_archive.2._round_val", 0,
              dpe + ":_archive.2._round_inv", 0
             );

  }


}



// 从tools下的创建DP工具画面来批量导入数据
// author:zhigang-li@siemens.com
//

// 从tools下的创建DP工具画面来批量导入数据
// author:zhigang-li@siemens.com
//
LIB_CreateDPTypeByImportTool(string dpType, string path, string SubSystemPrefix, string PLCType)
{
  dyn_dyn_string n;
  dyn_dyn_int t;
  string str;
  fileToString(path, str);   // 存储到str

  if (str == "")
  {
    DebugN("路径错误:" + path);
    return;
  }

  // 拆分成多行字符串 -> rows
  dyn_string rows = strsplit(str, "\n");

  // 表头第1行：描述
  dyn_string dsDescription = strsplit(rows[1], "\t");

  // 表头第2行：config1 。
  dyn_string dsConfig1 = strsplit(rows[2], "\t");

  // 表头第3行：config2 。
  dyn_string dsConfig2 = strsplit(rows[3], "\t");

  // 表头第4行：DATA TYPE , 数据类型 （DI, DO, AI, AO, WORD, DWORD ）
  dyn_string dsDATATYPE = strsplit(rows[4], "\t");

  dyn_dyn_string dsConfig_ERROR, dsConfig_STATE, dsConfig_CMD;
  dyn_dyn_int diConfig_ERROR_ID, diConfig_STATE_ID, diConfig_CMD_ID;

  // 处理地址与数据。表格的列 3~(n-1)
  for (int i = 7; i < dynlen(dsDescription)  ; i++) // 从后向前，去掉最后一个占位。
  {
    // 1. ERROR
    if (dsConfig1[i]  == "ERROR")
    {
      if (dsDATATYPE[i] == "AI")  // float
      {

        if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_AI_ALERT_" + PLCType))
        {
          DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_AI_ALERT_" + PLCType + " 不存在！无法继续，请检查。");
          return;
        }

        dynAppend(dsConfig_ERROR,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_AI_ALERT_" + PLCType)); // LIB_ATOM_MEP_DI_S7
        dynAppend(diConfig_ERROR_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));
      }

      if (dsDATATYPE[i] == "DI")  // float
      {
        if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_DI_ALERT_" + PLCType))
        {
          DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_DI_ALERT_" + PLCType + " 不存在！无法继续，请检查。");
          return;
        }

        dynAppend(dsConfig_ERROR,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_DI_ALERT_" + PLCType)); // LIB_ATOM_MEP_DI_S7
        dynAppend(diConfig_ERROR_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));
      }
    }

    // 2. STATE
    if (dsConfig1[i] == "STATE")
    {
      if (dsDATATYPE[i] == "AI")  // float
      {

        if (strpos(dsConfig2[i], "VAL_IN") >= 0)   // 主过程值
        {

          if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_AI_" + PLCType))
          {
            DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_AI_" + PLCType + " 不存在！无法继续，请检查。");
            return;
          }

          dynAppend(dsConfig_STATE,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_AI_" + PLCType)); //
          dynAppend(diConfig_STATE_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));
        }
        else
        {
          // 非主要的AI值。无需进行HOUR, MINUTE。

          if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_AI_SIMPLE_" + PLCType))
          {
            DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_AI_SIMPLE_" + PLCType + " 不存在！无法继续，请检查。");
            return;
          }

          dynAppend(dsConfig_STATE,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_AI_SIMPLE_" + PLCType)); //
          dynAppend(diConfig_STATE_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));
        }

      }

      if (dsDATATYPE[i] == "DI")  // DI
      {
        if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_DI_" + PLCType))
        {
          DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_DI_" + PLCType + " 不存在！无法继续，请检查。");
          return;
        }

        dynAppend(dsConfig_STATE,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_DI_" + PLCType)); //
        dynAppend(diConfig_STATE_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));

      }

      if (dsDATATYPE[i] == "WORD") // WORD -> INTPUT 16
      {
        if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_INPUT16_" + PLCType))
        {
          DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_INPUT16_" + PLCType + " 不存在！无法继续，请检查。");
          return;
        }

        dynAppend(dsConfig_STATE,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_INPUT16_" + PLCType)); //
        dynAppend(diConfig_STATE_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));

      }

      if (dsDATATYPE[i] == "DWORD")// DWORD -> INPUT 32
      {
        if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_INPUT32_" + PLCType))
        {
          DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_INPUT32_" + PLCType + " 不存在！无法继续，请检查。");
          return;
        }

        dynAppend(dsConfig_STATE,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_INPUT32_" + PLCType)); //
        dynAppend(diConfig_STATE_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));

      }

    }

    // 3. CMD
    if (dsConfig1[i] == "CMD")
    {

      if ((dsDATATYPE[i] == "DO") || (dsDATATYPE[i] == "PULSE_OPEN") || (dsDATATYPE[i] == "PULSE_CLOSE"))  // DO
      {
        if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_DO_" + PLCType))
        {
          DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_DO_" + PLCType + " 不存在！无法继续，请检查。");
          return;
        }

        dynAppend(dsConfig_CMD,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_DO_" + PLCType)); //
        dynAppend(diConfig_CMD_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));

      }

      if (dsDATATYPE[i] == "DWORD") // DWORD --> OUTPUT32
      {
        if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_OUTPUT32_" + PLCType))
        {
          DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_OUTPUT32_" + PLCType + " 不存在！无法继续，请检查。");
          return;
        }

        dynAppend(dsConfig_CMD,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_OUTPUT32_" + PLCType)); //
        dynAppend(diConfig_CMD_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));

      }

      if (dsDATATYPE[i] == "WORD") // WORD --> OUTPUT16
      {
        if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_OUTPUT16_" + PLCType))
        {
          DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_OUTPUT16_" + PLCType + " 不存在！无法继续，请检查。");
          return;
        }

        dynAppend(dsConfig_CMD,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_OUTPUT16_" + PLCType)); //
        dynAppend(diConfig_CMD_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));

      }

      if (dsDATATYPE[i] == "AO") // AO点位
      {
        if (!dpExists("_mp_LIB_ATOM_" + SubSystemPrefix + "_AO_" + PLCType))
        {
          DebugN("_mp_LIB_ATOM_" + SubSystemPrefix + "_AO_" + PLCType + " 不存在！无法继续，请检查。");
          return;
        }

        dynAppend(dsConfig_CMD,  makeDynString("", "",  dsConfig2[i], "LIB_ATOM_" + SubSystemPrefix + "_AO_" + PLCType));  //
        dynAppend(diConfig_CMD_ID, makeDynInt(0, 0, DPEL_TYPEREF, 0));

      }
    }

  }

  // 开始组合DP TYPE 结构体
  // dp type
  dynAppend(n,  makeDynString(dpType, "", "", ""));
  dynAppend(t,  makeDynInt(DPEL_STRUCT, 0, 0, 0));

  if (dynlen(diConfig_ERROR_ID) >= 1)
  {

    // ERROR
    dynAppend(n,  makeDynString("", "ERROR", "", ""));
    dynAppend(t,  makeDynInt(0, DPEL_STRUCT, 0, 0));

    // ERROR.SUM_ALERT
    dynAppend(n,  makeDynString("", "", "SUM_ALERT", ""));
    dynAppend(t,  makeDynInt(0, 0, DPEL_STRING, 0));

    // ERROR 结构体
    for (int i = 1; i <= dynlen(diConfig_ERROR_ID); i++)
    {
      dynAppend(n, dsConfig_ERROR[i]);
      dynAppend(t, diConfig_ERROR_ID[i]);
    }

  }

  // STATE
  if (dynlen(diConfig_STATE_ID) >= 1)
  {

    dynAppend(n,  makeDynString("", "STATE", "", ""));
    dynAppend(t,  makeDynInt(0, DPEL_STRUCT, 0, 0));

    // STATE 结构体
    for (int i = 1; i <= dynlen(diConfig_STATE_ID); i++)
    {

      dynAppend(n, dsConfig_STATE[i]);
      dynAppend(t, diConfig_STATE_ID[i]);
    }

  }


  // CMD
  if (dynlen(diConfig_CMD_ID) >= 1)
  {

    dynAppend(n,  makeDynString("", "CMD", "", ""));
    dynAppend(t,  makeDynInt(0, DPEL_STRUCT, 0, 0));

    // CMD 结构体
    for (int i = 1; i <= dynlen(diConfig_CMD_ID); i++)
    {

      dynAppend(n, dsConfig_CMD[i]);
      dynAppend(t, diConfig_CMD_ID[i]);
    }

  }

  // GENERAL
  //（仅当CMD下面有数据，才生成GENERAL.NOTE 的操作日志dpe）
  if (dynlen(diConfig_CMD_ID) >= 1)
  {
    dynAppend(n,  makeDynString("", "GENERAL", "", ""));
    dynAppend(t,  makeDynInt(0, DPEL_STRUCT, 0, 0));

    dynAppend(n,  makeDynString("", "", "NOTE", ""));
    dynAppend(t,  makeDynInt(0, 0, DPEL_DYN_STRING, 0));


  }

  int iRet = dpTypeCreate(n, t);

  DebugN(path);
  DebugN(dpType);
  DebugN(SubSystemPrefix);
  DebugN(PLCType);
  DebugN("执行完毕！");

}





////////////////////////////////////////////////////////////////// AB
////////////////////////////////////////////////////////////////// AB

LIB_SetABAddress(string dpe,  // dpe
                 string addr, // dpe address. 格式：DB103.DBX0.0
                 string PLC,  //  "F1_FAB_R_UPW_PLC01." , PLC DP
                 int driverNumber, //  3 ,   驱动号
                 string pollingGroup = "_AB_polling_1s")
{

  if (PLC == "")
  {
    DebugN("LIB_SetABAddress 未指定 参数PLC");
    return;
  }

  bool active = ((addr == "NULL") || (addr == "")) ? false : true;

  dpSetWait(dpe + ":_address.._active", false);

  // 禁用地址。
  if (!active)
  {
    return;
  }

  string _reference;


  string sFloat;


  /*    if( (dpElementType(dpe) == DPEL_FLOAT) &&  strpos(addr,"F") ==  -1 ) // addr 未定义F后缀的浮点。
      {
        sFloat = "F";
      }
  */
  /*
  _address.. 地址配置项说明：

  _active        地址激活
  _connection    --
  _datatype      数据类型，INT32, FLOAT... 继承自 Master DP
  _direction     读写方向. 继承自 Master DP
  _drv_ident     驱动"MODBUS", "S7"
  _internal      未使用
  _lowlevel      驱动层程序的新旧值比较， 继承自Master DP
  _mode          未使用
  _offset        需要使用的偏移量，如bit偏移量，未使用
  _poll_group    轮询组
  _reference     地址。
  _start         未使用
  _subindex      modbus等需要使用的偏移量，如bit偏移量
  _type          DPCONFIG_PERIPH_ADDR_MAIN 16：物理地址。 DPCONFIG_NONE 0：禁用物理地址。


  _distrib..地址配置项说明：
  _driver       分配驱动号
  _type         驱动激活 DPCONFIG_DISTRIBUTION_INFO 56 driver allocation
  */

  dpSetWait(dpe + ":_distrib.._driver",  driverNumber,
            dpe + ":_distrib.._type",    active ? DPCONFIG_DISTRIBUTION_INFO : DPCONFIG_NONE);

  dpSetWait(dpe + ":_address.._reference",  PLC +  addr + sFloat);
  dpSetWait(dpe + ":_address.._active",     active ? true : false);
  dpSetWait(dpe + ":_address.._poll_group", pollingGroup);

  if (!active)
  {
    DebugN(dpe + "已经禁用地址！");
    return;
  }


  dpGet(dpe + ":_address.._reference", _reference);

  //delay(0,1);

  if (_reference == (PLC +  addr))
  {
    DebugN(dpe,  _reference, "OK");
  }
  else
  {
    if (shapeExists("msg"))      msg.append(dpe);

    DebugN(dpe, "期望 " + (PLC +  addr) + "; 实际 " + _reference, "！！！！！！ERROR ");
  }
}


//////////////////////////////
////////////////////////HL
LIB_SetFloatAlert(string dpe,
                  string alertClassLL, string alertClassL, string alertClassH, string alertClassHH,
                  float LimitLL, float LimitL, float LimitH, float LimitLHH,
                  string Des)
{

  dyn_float limits;

  dyn_string alerttext;

  dyn_string alertclass;

  dyn_bool limitsincl;

  int rc;

  dyn_errClass err;

//limits=makeDynFloat(20, 40 ,60 ,80);

  alerttext = makeDynString(Des + ":LL报警", Des + ":L报警", Des + ":正常", Des + ":H报警", Des + ":HH报警");

//alertclass=makeDynString("LIB_Alert_Level1.", "LIB_Alert_Level2.");


  rc = dpSetTimed(0, dpe + ":_alert_hdl.._type", DPCONFIG_ALERT_NONBINARYSIGNAL,

                  dpe + ":_alert_hdl.1._type", 4,

                  dpe + ":_alert_hdl.2._type", 4,

                  dpe + ":_alert_hdl.3._type", 4,

                  dpe + ":_alert_hdl.4._type", 4,

                  dpe + ":_alert_hdl.5._type", 4,

                  dpe + ":_alert_hdl.1._u_limit", LimitLL,

                  dpe + ":_alert_hdl.2._u_limit", LimitL,

                  dpe + ":_alert_hdl.2._l_limit", LimitLL,

                  dpe + ":_alert_hdl.3._u_limit", LimitH,

                  dpe + ":_alert_hdl.3._l_limit", LimitL,

                  dpe + ":_alert_hdl.4._u_limit", LimitLHH,

                  dpe + ":_alert_hdl.4._l_limit", LimitH,

                  dpe + ":_alert_hdl.5._l_limit", LimitLHH,

                  dpe + ":_alert_hdl.1._u_incl", 1,

                  dpe + ":_alert_hdl.1._l_incl", 1,

                  dpe + ":_alert_hdl.2._u_incl", 1,

                  dpe + ":_alert_hdl.2._l_incl", 0,

                  dpe + ":_alert_hdl.3._u_incl", 1,

                  dpe + ":_alert_hdl.3._l_incl", 0,

                  dpe + ":_alert_hdl.4._u_incl", 1,

                  dpe + ":_alert_hdl.4._l_incl", 0,

                  dpe + ":_alert_hdl.5._u_incl", 1,

                  dpe + ":_alert_hdl.5._l_incl", 0,


                  dpe + ":_alert_hdl.1._text", alerttext[1],

                  dpe + ":_alert_hdl.2._text", alerttext[2],

                  dpe + ":_alert_hdl.3._text", alerttext[3],

                  dpe + ":_alert_hdl.4._text", alerttext[4],

                  dpe + ":_alert_hdl.5._text", alerttext[5],

                  dpe + ":_alert_hdl.1._class", alertClassLL,

                  dpe + ":_alert_hdl.2._class", alertClassL,

                  dpe + ":_alert_hdl.4._class", alertClassH,

                  dpe + ":_alert_hdl.5._class", alertClassHH,

                  dpe + ":_alert_hdl.._orig_hdl", TRUE,

                  dpe + ":_alert_hdl.._active", false
                 );

  err = getLastError();

  if (dynlen(err) > 0)

  {
    throwError(err);
  }

  DebugN("done", rc);

  if (rc == -1)
  {
    DebugN("报警设置错误");
  }
  else
  {
    DebugN("报警设置成功");
  }



}




LIB_SetS7Address(string dpe,  // dpe
                 string addr, // dpe address. 格式：DB103.DBX0.0
                 string PLC,  //  "F1_FAB_R_UPW_PLC01." , PLC DP
                 int driverNumber, //  3 ,   驱动号
                 string pollingGroup = "_siemens_polling_1s")
{

  if (PLC == "")
  {
    DebugN("LIB_SetS7Address 未指定 参数PLC");
    return;
  }

  //以前是addr地址为空则设置地址禁用。
  // 现在是地址为空则去除掉address属性。
  if ((addr == "NULL") || (addr == ""))
  {
    dpSetWait(dpe + ":_distrib.._driver",  DPCONFIG_NONE);
    dpSetWait(dpe + ":_address.._type",  DPCONFIG_NONE);

    return;
  }

  dpSetWait(dpe + ":_address.._active", false);


  string _reference;


  string sFloat;


  if ((dpElementType(dpe) == DPEL_FLOAT) &&  strpos(addr, "F") ==  -1) // addr 未定义F后缀的浮点。
  {
    sFloat = "F";
  }

  /*
  _address.. 地址配置项说明：

  _active        地址激活
  _connection    --
  _datatype      数据类型，INT32, FLOAT... 继承自 Master DP
  _direction     读写方向. 继承自 Master DP
  _drv_ident     驱动"MODBUS", "S7"
  _internal      未使用
  _lowlevel      驱动层程序的新旧值比较， 继承自Master DP
  _mode          未使用
  _offset        需要使用的偏移量，如bit偏移量，未使用
  _poll_group    轮询组
  _reference     地址。
  _start         未使用
  _subindex      modbus等需要使用的偏移量，如bit偏移量
  _type          DPCONFIG_PERIPH_ADDR_MAIN 16：物理地址。 DPCONFIG_NONE 0：禁用物理地址。

  _distrib..地址配置项说明：
  _driver       分配驱动号
  _type         驱动激活 DPCONFIG_DISTRIBUTION_INFO 56 driver allocation
  */

  dpSetWait(dpe + ":_distrib.._driver",  driverNumber,
            dpe + ":_distrib.._type",    DPCONFIG_DISTRIBUTION_INFO);

  dpSetWait(dpe + ":_address.._reference",  PLC + "." + addr + sFloat,
            dpe + ":_address.._active",       true,
            dpe + ":_address.._poll_group", pollingGroup);

  dpGet(dpe + ":_address.._reference", _reference);

  //delay(0,1);

  if (_reference == (PLC + "." + addr + sFloat))
  {
    DebugN(dpe,  _reference, "OK");
  }
  else
  {
    if (shapeExists("msg"))      msg.append(dpe);

    DebugN(dpe, "期望 " + (PLC + "." + addr + sFloat) + "; 实际 " + _reference, "!!!! ERROR ");
  }
}


// 鼠标悬停的3D移动效果
LIB_MouseHover(bool enter)
{
  int x, y;

  if (enter)
  {

    getValue("", "position", x, y);
    x  = x - 2;
    y =  y - 2;
    setValue("", "position", x, y);
    this.shadowOffset(4, 4);
  }
  else
  {

    getValue("", "position", x, y);
    x  = x + 2;
    y =  y + 2;
    setValue("", "position", x, y);
    this.shadowOffset(2, 2);
  }
}
LIB_SetUserWriteRangeFloat(string dpe, int permissionLevel, float min, float max)
{
  dpSetWait(dpe + ":_u_range.._type", DPCONFIG_USER_RANGECHECK,
            dpe + ":_u_range." + permissionLevel + "._type", DPDETAIL_RANGETYPE_MINMAX,
            dpe + ":_u_range." + permissionLevel + "._max", max,
            dpe + ":_u_range." + permissionLevel + "._min", min,
            dpe + ":_u_range." + permissionLevel + "._incl_max", TRUE,
            dpe + ":_u_range." + permissionLevel + "._incl_min", TRUE);

}
LIB_SetUserWriteRangeINT16(string dpe, int permissionLevel, int min, int max)
{


  dpSetWait(dpe + ":_u_range.._type", DPCONFIG_USER_RANGECHECK,
            dpe + ":_u_range." + permissionLevel + "._type", DPDETAIL_RANGETYPE_MINMAX,
            dpe + ":_u_range." + permissionLevel + "._max", max,
            dpe + ":_u_range." + permissionLevel + "._min", min,
            dpe + ":_u_range." + permissionLevel + "._incl_max", TRUE,
            dpe + ":_u_range." + permissionLevel + "._incl_min", TRUE);

}
// 操作记录功能
// type = 1: dpSetOnlySend, 2:dpSetTimeExpired, 3,dpSetDone
// const string G_DPSET_ONLY_SEND = 1;
// const string G_DPSET_TIME_EXPIRED = 2;
// const string G_DPSET_DONE = 3;
// removeTheLastOnlySendMsg,删除最后一次的“仅发送”的那则预留的消息。补充进来一个真实的消息。
LIB_OperateRecord(string dpe, string MsgTxt, int type, bool removeTheLastOnlySendMsg = false) // // type = 1: dpSetOnlySend, 2:dpSetTimeExpired, 3,dpSetDone
{

  string dp = dpSubStr(dpe, DPSUB_SYS_DP);

  // 计数器 监测了5s，仍然没有下发成功！
  if (dpExists(dp + ".GENERAL.NOTE"))
  {
    dyn_string dsNotes;
    dpGet(dp + ".GENERAL.NOTE", dsNotes);
    string userName;
    dpGet(myUiDpName() + ".UserName", userName);
    string sComment;

    if (type == G_DPSET_ONLY_SEND)
    {
      sComment = getCatStr("LIB", "dpSetOnlySend");
    }
    else if (type == G_DPSET_TIME_EXPIRED)
    {
      sComment = getCatStr("LIB", "dpSetTimeExpired");
    }
    else if (type == G_DPSET_DONE)
    {
      sComment = getCatStr("LIB", "dpSetDone");
    }

    string note = formatTime("%c", getCurrentTime()) + "|" + userName + "|" + MsgTxt  + "|" + sComment; // format : Time|User|Data|Comment

    // 删除最后一次的“仅发送”的那则预留的消息。补充进来一个真实的消息。
    if (removeTheLastOnlySendMsg) dynRemove(dsNotes, 1);

    dynInsertAt(dsNotes, note, 1);

    if (dynlen(dsNotes) > 3000)
    {
      dynRemove(dsNotes, 3000);
    }

    dpSet(dp + ".GENERAL.NOTE", dsNotes);
  }
}

void LIB_ChildPanelOn(string dp)
{
  langString lsWindowText;


  if (dpExists(dp + "."))
  {

    lsWindowText = dpGetDescription(dp + ".");
    string panelPath = LIB_GetDeviceDetailsPath(dp);
    ChildPanelOnRelativ(panelPath,  // etc. "objects/LIB/common/PumpDetails/main.xml"
                        lsWindowText + "("+dp+")" ,
                        makeDynString("$dp:" + dp), 100, -200);


  }
  else
  {
    ChildPanelOnModal("vision/messages/MessageInfo.pnl", "", makeDynString("$1:Datapoint does not exist, " + dp,
                      "$2:确定",
                      "$3:取消"), 1902 / 2, 1080 / 2);
  }

}

//...............................//
void LIB_ChildPanelOn2(string dp)
{
  langString lsWindowText;


  lsWindowText = dpGetDescription(dp + ".");
  string panelPath = LIB_GetDeviceDetailsPath(dp);
  ChildPanelOnRelativ(panelPath,  // etc. "objects/LIB/common/PumpDetails/main2.xml"
                      lsWindowText + " -- " + panelPath,
                      makeDynString("$dp:" + dp), 100, -200);
}

//...............................//

//...............................//
void LIB_ChildPanelOn3(string dp)
{
  langString lsWindowText;


  lsWindowText = dpGetDescription(dp + ".");
  string panelPath = LIB_GetDeviceDetailsPath(dp);
  ChildPanelOnRelativ(panelPath,  // etc. "objects/LIB/common/PumpDetails/main2.xml"
                      lsWindowText + " -- " + panelPath,
                      makeDynString("$dp:" + dp), 100, -100);
}


void LIB_ChildPanelOnPID(string dp, string dp_pv)
{
  langString lsWindowText;


  lsWindowText = dpGetDescription(dp + ".");
  string panelPath = LIB_GetDeviceDetailsPath(dp);
  ChildPanelOnRelativ(panelPath,  // etc. "objects/LIB/common/PumpDetails/main.xml"
                      lsWindowText + " -- " + panelPath,
                      makeDynString("$dp:" + dp, "$dp_pv:" + dp_pv), 100, -100);
}


string LIB_Tooltip(string dp)
{
  string tooltipTxt = dp + "<br>"  ;

  // 显示tooltip信息

  if (dpExists(dp + "."))
  {
    tooltipTxt +=  dpGetDescription(dp + ".")  ;
  }

  return tooltipTxt;
}






// LIB_PopupComplex 右键菜单,
// 应该被弃用。仅保留兼容性。
LIB_PopupComplex(string dp)
{
  //
  // same to single value as well.
  LIB_PopupSingleValue(dp);

}



//--------------------------------------------------------------------------------
// LIB 界面的变量对话框鼠标悬停显示的帮助信息
//

LIB_PopupObject(string dp)
{
  int answer;

  dyn_string   txt ;



  // dynAppend(txt, "PUSH_BUTTON," + getCatStr("LIB", "configAvailable") + ",1," + (getUserPermission(7) ? "1" : "0")); // permission bit 7 ：LIB Trend view auth.
  dynAppend(txt, "PUSH_BUTTON," + getCatStr("LIB", "objectConfig") + ",2," + (getUserPermission(7) ? "1" : "0")); // permission bit 7 ：LIB Trend view auth.
  dynAppend(txt, "PUSH_BUTTON," + getCatStr("LIB", "onlineData") + ",3," + (getUserPermission(7) ? "1" : "0")); // permission bit 7 ：LIB Trend view auth.
  // 执行右键弹出的指令代码
  popupMenu(txt, answer);

  if (answer == 1)
  {

    string sObjectPath = "objects_parts/STD_OBJECTS/COMMON_prop.pnl";
    string path =  getPath(PANELS_REL_PATH, sObjectPath);

    if (path != "")                                                   // found -> use it
      ChildPanelOnCentral(sObjectPath, getCatStr("STD_Symbols", "PARA:") + dpSubStr(dp, DPSUB_DP), makeDynString("$dpe:" + dp, "$dp:" + dpSubStr(dp, DPSUB_SYS_DP)));
    else
      ChildPanelOnCentral("vision/MessageWarning", getCatStr("STD_Symbols", "PARA:") + getCatStr("va", "e13"), makeDynString(getCatStr("STD_Symbols", "panelmissing") + sObjectPath, getCatStr("general", "cancel")));
  }

  else if (answer == 2)
  {

    string title = getCatStr("LIB", "dpParaTitle");
    ChildPanelOnCentralModal("LIB_Para.xml", title + "(DP:" + dp + ") -- LIB_Para.xml",
                             makeDynString("$DpFilterChangeable:FALSE",
                                           "$OpenWithDp:" + dp));


  }
  else if (answer == 3)
  {
    string sObjectPath = "objects_parts/STD_OBJECTS/COMMON_info.pnl";
    string path =  getPath(PANELS_REL_PATH, sObjectPath);  // found -> use it

    if (path != "")
      ChildPanelOnCentral(sObjectPath, getCatStr("STD_Symbols", "INFO:") + dpSubStr(dp, DPSUB_DP), makeDynString("$dpe:" + dp, "$dp:" + dpSubStr(dp, DPSUB_SYS_DP)));
    else
      ChildPanelOnCentral("vision/MessageWarning", getCatStr("STD_Symbols", "INFO:") + getCatStr("va", "e13"), makeDynString(getCatStr("STD_Symbols", "panelmissing") + sObjectPath, getCatStr("general", "cancel")));

  }


}

LIB_PopupSingleValue(string dp, bool displayDetails = true)
{
  int answer;

  dyn_string   txt ;

  // 点击右键只有报警的属性才能显示“修改参数”,允许是_active true或false
  if (dpExists(dp + ":_alert_hdl.._active"))
  {
    int _type ;
    dpGet(dp + ":_alert_hdl.._type", _type);

    if (_type != 0)
    {
      // _type==0  如果没有配置任何类型的报警配置，不显示这个菜单项。
      dynAppend(txt, "PUSH_BUTTON," + getCatStr("LIB", "configAvailable") + ",1," + (getUserPermission(4) ? "1" : "0")); //
    }
  }

  if (displayDetails)
  {
    // 详细资料...
    dynAppend(txt, "PUSH_BUTTON," + getCatStr("LIB", "objectConfig") + ",2," + (getUserPermission(7) ? "1" : "0"));

  }

  if (strpos(dp, ".CMD.") > 0)
  {

    // 趋势缓冲区的二级菜单
    dynAppend(txt, makeDynString("PUSH_BUTTON, " + getCatStr("LIB", "menuOpeHis") + ", 9 , 1"));

  }

  // 趋势
  dynAppend(txt, makeDynString("PUSH_BUTTON, " + getCatStr("STD_Symbols", "trends") + ", " + 3 + ", 1"));
  // 趋势及趋势缓冲区
  dynAppend(txt, makeDynString("CASCADE_BUTTON, " + getCatStr("STD_Symbols", "trendbuffer") + ", "  + (getUserPermission(7) ? "1" : "0"), // Operator
                               "SEPARATOR"));



  // 趋势缓冲区的二级菜单
  dynAppend(txt, makeDynString(getCatStr("STD_Symbols", "trendbuffer")));

  dynAppend(txt, makeDynString("PUSH_BUTTON," + getCatStr("STD_Symbols", "addtotrendbuffer") + ", 2000, 1"));

  dynAppend(txt, makeDynString("PUSH_BUTTON," + getCatStr("STD_Symbols", "workwithtrendbuffer") + ", 2001 , " + (getUserPermission(7) ? "1" : "0"),
                               "PUSH_BUTTON," + getCatStr("STD_Symbols", "cleartrendbuffer") + ", 2999 , " + (getUserPermission(7) ? "1" : "0"),
                               "SEPARATOR",
                               "PUSH_BUTTON", getCatStr("STD_Symbols", "showtrendbuffer") + ", 2002," + (getUserPermission(7) ? "1" : "0")));

  // 显示趋势缓冲区三级菜单



  // 执行右键弹出的指令代码
  popupMenu(txt, answer);
  dyn_string dsCurves;

  string sAppend;
  dyn_string Curve;      // dynstring with all curves for trendgroup
  string Trendgroup = "MIN-MAX";      // which trendgroup

  if (!globalExists("gTrendDpBuffer"))
  {
    addGlobal("gTrendDpBuffer", DYN_STRING_VAR); // define if ! defined!!
  }

  string dpName = dpSubStr(dp, DPSUB_SYS_DP);

  if (answer == 1)
  {
    LIB_mpDaPara(dp);

  }
  else  if (answer == 2)
  {
    string title = getCatStr("LIB", "dpParaTitle");
    ChildPanelOnCentralModal("LIB_Para.xml", title + "(DP:" + dp + ") -- LIB_Para.xml",
                             makeDynString("$DpFilterChangeable:FALSE",
                                           "$OpenWithDp:" + dp));
  }
  else if (answer == 3)
  {
    // 单条曲线
    dynClear(gTrendDpBuffer);
    dynAppend(gTrendDpBuffer, dp);

    // 释放进程锁，更新趋势缓冲区的功能。
    semRelease("SEM_UPDATE_TREND_BUFFER");

    ChildPanelOnCentral("tools/LIB_Trend.xml",  dp,  makeDynString(""));                                                                       // -> trendcurvenames [ac.cat]

  }

  else if (answer == 9)
  {
    // 操作日志

    ChildPanelOnCentralModal("vision/aes/AEScreenCMD.xml", "历史操作记录查询(" + dp + ")",
                             makeDynString("$dpe:" + dp));

  }

  else if (answer >= 2000 && answer < 3000)
  {


    if (answer == 2000)
    {

      if (dynContains(gTrendDpBuffer, dp) <= 0)
      {
        if (dynlen(gTrendDpBuffer) >= 16)
        {
          ChildPanelOnCentralModal("vision/messages/LIB_MessageInfo.pnl", "",
                                   makeDynString("$nok:" + getCatStr("LIB", "CANCEL"), "$ok:" +  getCatStr("LIB", "OK"), "$text:" + getCatStr("LIB", "max8curves")));
          return;

        }
        else
        {

          dynAppend(gTrendDpBuffer, dp);
        }


        // 释放进程锁，更新趋势缓冲区的功能。
        semRelease("SEM_UPDATE_TREND_BUFFER");
      }
    }
    else if (answer == 2001)                                                             // 编辑趋势缓冲区
    {
      ChildPanelOnCentral("objects_parts/STD_OBJECTS/COMMON_trendbuffer.pnl", getCatStr("STD_Symbols", "Trendbuffer"), "");     // open Panel
    }
    else if (answer == 2999)                                                             // 清空趋势缓冲区
    {
      dynClear(gTrendDpBuffer);
      ChildPanelOnCentral("vision/MessageWarning", getCatStr("profibus", "hinweis"), makeDynString(getCatStr("STD_Symbols", "trendbuffercleared"), getCatStr("general", "ok")));

      // 释放进程锁，更新趋势缓冲区的功能。
      semRelease("SEM_UPDATE_TREND_BUFFER");

    }
    else if (answer == 2002)  // 显示趋势
    {
      // 显示趋势缓冲区
      ChildPanelOnCentralModal("tools/LIB_Trend.xml", "",  makeDynString(""));
    }
  }

}



LIB_PopupSingleValueWithoutTrend(string dp)
{
  int answer;

  dyn_string   txt ;


  // 修改参数 不再需要。
  // dynAppend(txt, "PUSH_BUTTON," + getCatStr("LIB", "configAvailable") + ",1," + (getUserPermission(4) ? "1" : "0")); // permission bit 7 ：LIB Trend view auth.

  // 详细资料...
  dynAppend(txt, "PUSH_BUTTON," + getCatStr("LIB", "objectConfig") + ",2," + (getUserPermission(7) ? "1" : "0"));




  if (strpos(dp, ".CMD.") > 0)
  {

    // 趋势缓冲区的二级菜单
    dynAppend(txt, makeDynString("PUSH_BUTTON, " + getCatStr("LIB", "menuOpeHis") + ", 9 , 1"));

  }

  // 执行右键弹出的指令代码
  popupMenu(txt, answer);

  if (answer == 1)
  {
    LIB_mpDaPara(dp);
  }

  else if (answer == 2)
  {
    string title = getCatStr("LIB", "dpParaTitle");

    ChildPanelOnCentralModal("LIB_Para.xml", title + "(DP:" + dp + ") -- LIB_Para.xml",
                             makeDynString("$DpFilterChangeable:FALSE",
                                           "$OpenWithDp:" + dp));

  }
  else if (answer == 9)                                                    // selected: MIN-MAX with spezified data
  {
    // 操作日志
    string dpName = dpSubStr(dp, DPSUB_SYS_DP);
    ChildPanelOnCentralModal("objects/LIB/common/noteDetails.xml",
                             dp + " -- objects/LIB/common/noteDetails.xml",
                             makeDynString("$dp:" + dpName));
    // -> trendcurvenames [ac.cat]
  }

}

// LIB 在线修改参数配置
LIB_mpDaPara(string dpstr)
{
  string sConf = dpSubStr(dpstr, DPSUB_CONF);

  if (sConf != "")
    strreplace(dpstr, sConf, "");

//   ChildPanelOnCentralModal("para/mp_common_prop.pnl", getCatStr("STD_Symbols", "PARA:") + " -- para/mp_common_prop.pnl",
//                            makeDynString("$dpe:" + dpstr));
//    string dpe = $dpe;

  string title = getCatStr("FMCS", "dpParaTitle");

  ChildPanelOnCentralModal("tools/LIB_ParaAlert.xml", "(DP:" + dpstr + ") -- FMCS_Para.xml",
                           makeDynString("$DpFilterChangeable:FALSE",
                                         "$OpenWithDp:" + dpstr + ":_alert_hdl"));

}






string LIB_DPName_2(string WinCCOADpName)
{
  string ret;
  string LIBName = dpSubStr(WinCCOADpName, DPSUB_DP);  //System1:CRA_1_L10_MAU01_TCV01
  strreplace(LIBName, "_", "-");
  dyn_string ds = strsplit(LIBName, "-");

  if (dynlen(ds))
  {

    ret = ds[dynlen(ds) - 1] + "-" + ds[dynlen(ds)];
    return ret; // CRA-1-L10-MAU01-TCV01 -> MAU01-TCV01
  }
  else
  {
    return "!NO-DP-NAME!";
  }
}

string LIB_DPName_3(string WinCCOADpName)
{
  string ret;
  string LIBName = dpSubStr(WinCCOADpName, DPSUB_DP);  //System1:CRA_1_L10_MAU01_TCV01
  strreplace(LIBName, "_", "-");
  dyn_string ds = strsplit(LIBName, "-");

  if (dynlen(ds))
  {

    ret = ds[dynlen(ds) - 2] + "-" + ds[dynlen(ds) - 1] + "-" + ds[dynlen(ds)];
    return ret; // CRA-1-L10-MAU01-TCV01 -> MAU01-TCV01
  }
  else
  {
    return "!NO-DP-NAME!";
  }
}

string LIB_DPName_4(string WinCCOADpName)
{
  string ret;
  string LIBName = dpSubStr(WinCCOADpName, DPSUB_DP);  //System1:CRA_1_L10_MAU01_TCV01
  strreplace(LIBName, "_", "-");
  dyn_string ds = strsplit(LIBName, "-");

  if (dynlen(ds))
  {

    ret = ds[dynlen(ds) - 3] + "-" + ds[dynlen(ds) - 2] + "-" + ds[dynlen(ds) - 1] + "-" + ds[dynlen(ds)];
    return ret; // CRA-1-L10-MAU01-TCV01 -> MAU01-TCV01
  }
  else
  {
    return "!NO-DP-NAME!";
  }
}

string LIB_DPName_1(string WinCCOADpName)
{
  string ret;
  string LIBName = dpSubStr(WinCCOADpName, DPSUB_DP);  //System1:CRA_1_L10_MAU01_TCV01

  dyn_string ds = strsplit(LIBName, "_");

  if (dynlen(ds))
  {

    ret =     ds[dynlen(ds)];
    return ret; // CRA-1-L10-MAU01-TCV01 -> MAU01-TCV01
  }
  else
  {
    return "!NO-DP-NAME!";
  }
}

string LIB_DPNameAll(string WinCCOADpName)
{
  string ret;
  string LIBName = dpSubStr(WinCCOADpName, DPSUB_DP);  //System1:CRA_1_L10_MAU01_TCV01
  strreplace(LIBName, "_", "-");
  return LIBName; // CRA-1-L10-MAU01-TCV01
}

LIB_SetDPEWriteAuth(string dpe, int authWrite)
{
  dpSetWait(dpe + ":_auth.._type", DPCONFIG_AUTH,
            dpe + ":_auth._original._write", authWrite);
}



LIB_CheckArchive(string dpe, string addr)
{
  bool disabled = ((addr == "NULL") || (addr == "")) ? true : false;

  if (disabled)
  {
    //_type=0, DPCONFIG_NONE No archive settings

    bool _type, _archive;
    dpGet(dpe + ":_archive.._type", _type);
    dpGet(dpe + ":_archive.._archive", _archive);

    if (_type != DPCONFIG_NONE)
    {
      dpSetWait(dpe + ":_archive.._type", DPCONFIG_NONE);
    }

    if (_archive)
    {
      dpSetWait(dpe + ":_archive.._archive", FALSE);
    }
  }
}




/*
archiveClass 格式： "_ValueArchive_1"
smoothType:
DPATTR_VALUE_SMOOTH	0	值滤波
DPATTR_TIME_SMOOTH	1	时间滤波
DPATTR_TIME_AND_VALUE_SMOOTH	2	组合滤波- AND
DPATTR_TIME_OR_VALUE_SMOOTH	3	组合滤波- OR
DPATTR_COMPARE_OLD_NEW	4	新旧比较
DPATTR_OLD_NEW_AND_TIME_SMOOTH	5	新旧比较 AND 时间
DPATTR_OLD_NEW_OR_TIME_SMOOTH	6	新旧比较 OR 时间
DPATTR_VALUE_REL_SMOOTH	7	相对死区值滤波
DPATTR_TIME_AND_VALUE_REL_SMOOTH	8	滤波时间 AND 相对死区值
DPATTR_TIME_OR_VALUE_REL_SMOOTH	9	滤波时间 OR 相对死区值

tolerance: 死区
timeInterval: 滤波时间
 */
LIB_SetAnalogArchive(string dpe, string archiveClass, int smoothType, float tolerance, time timeInterval)
{

  //_type=0, DPCONFIG_NONE No archive settings

  bool bAddress;
  dpGet(dpe + ":_address.._active", bAddress);

  if (bAddress)
  {

    dpSetWait(dpe + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO,
              dpe + ":_archive.._archive", false);

    dpSetWait(dpe + ":_archive.._type", DPCONFIG_DB_ARCHIVEINFO, // 45
              dpe + ":_archive.._archive", true,
              dpe + ":_archive.1._type", DPATTR_ARCH_PROC_SIMPLESM, // DPATTR_TIME_OR_VALUE_SMOOTH:3 Archive smoothing is used
              dpe + ":_archive.1._class", archiveClass,
              dpe + ":_archive.1._std_type", DPATTR_TIME_OR_VALUE_REL_SMOOTH, // 9,
              dpe + ":_archive.1._std_tol", tolerance,
              dpe + ":_archive.1._std_time", timeInterval
             );

  }
  else
  {
    string mAvg, mMax, mMin, hAvg, hMax, hMin;
    mAvg = dpe;
    mMax = dpe;
    mMin = dpe;
    hAvg = dpe;
    hMax = dpe;
    hMin = dpe;

    dpSetWait(dpe + ":_archive.._type", DPCONFIG_NONE);
    dpSetWait(dpe + ":_archive.._archive", false);

    strreplace(mAvg, ".VALUE", ".MINUTE.AVG");
    dpSetWait(mAvg + ":_archive.._type", DPCONFIG_NONE);
    dpSetWait(mAvg + ":_archive.._archive", false);

    strreplace(mMax, ".VALUE", ".MINUTE.MAX");
    dpSetWait(mMax + ":_archive.._type", DPCONFIG_NONE);
    dpSetWait(mMax + ":_archive.._archive", false);

    strreplace(mMin, ".VALUE", ".MINUTE.MIN");
    dpSetWait(mMin + ":_archive.._type", DPCONFIG_NONE);
    dpSetWait(mMin + ":_archive.._archive", false);

    strreplace(hAvg, ".VALUE", ".HOUR.AVG");
    dpSetWait(hAvg + ":_archive.._type", DPCONFIG_NONE);
    dpSetWait(hAvg + ":_archive.._archive", false);

    strreplace(hMax, ".VALUE", ".HOUR.MAX");
    dpSetWait(hMax + ":_archive.._type", DPCONFIG_NONE);
    dpSetWait(hMax + ":_archive.._archive", false);

    strreplace(hMin, ".VALUE", ".HOUR.MIN");
    dpSetWait(hMin + ":_archive.._type", DPCONFIG_NONE);
    dpSetWait(hMin + ":_archive.._archive", false);
//
//         DebugN("禁用归档：" + dpe);
//         DebugN("禁用归档：" + mAvg);
//         DebugN("禁用归档：" + mMax);
//         DebugN("禁用归档：" + mMin);
//         DebugN("禁用归档：" + hAvg);
//         DebugN("禁用归档：" + hMin);
//         DebugN("禁用归档：" + hMax);
  }



}

// 为报警添加报警文本 和报警等级
LIB_SetBoolAlert(string dpe, string alertClass, string alertText1)
{
  // definition start
  //_da_alert_hdl:BOOL
  // variables definition
  bool _active;
  int i;
  int _type;

  dpGet(dpe + ":_alert_hdl.._active", _active);
  dpGet(dpe + ":_alert_hdl.._type", _type);


  if (_type != DPCONFIG_ALERT_BINARYSIGNAL)
  {
    // dpSetWait(dpe + ":_alert_hdl.._type", DPCONFIG_ALERT_BINARYSIGNAL);
    dpSetTimed(0, dpe + ":_alert_hdl.._type", DPCONFIG_ALERT_BINARYSIGNAL); // 对于_alert_hdl需要使用dpSetTimed来处理！
  }


  if (_active)
  {
//     dpSetWait(dpe + ":_alert_hdl.._ack", 2);
//     dpSetWait(dpe + ":_alert_hdl.._active", 0);
//     dpSetWait(dpe + ":_alert_hdl.._ack", 2);
//
    // 对于_alert_hdl需要使用dpSetTimed来处理！
    dpSetTimed(0, dpe + ":_alert_hdl.._ack", 2);
    dpSetTimed(0, dpe + ":_alert_hdl.._active", 0);
    dpSetTimed(0, dpe + ":_alert_hdl.._ack", 2);

  }

//
//   dpSetWait( /*dpe+":_alert_hdl.._text0",alertText0, */                                         // 报警（正常）的文字
//     dpe + ":_alert_hdl.._text1", alertText1, // 处于报警状态的文字,
//     dpe + ":_alert_hdl.._class", alertClass
//   );
//
  dpSetTimed(0, /*dpe+":_alert_hdl.._text0",alertText0, */                                         // 报警（正常）的文字
             dpe + ":_alert_hdl.._text1", alertText1, // 处于报警状态的文字,
             dpe + ":_alert_hdl.._class", alertClass
            );



  // dpSetWait(dpe + ":_alert_hdl.._active", true);
  dpSetTimed(0, dpe + ":_alert_hdl.._active", true);

  // 处理错误！
  dyn_errClass err = getLastError();

  if (dynlen(err) > 0)
  {
    throwError(err);
  }

}


string showDpCopyError(int error)
{
  mapping m;

  m["-1"] =	"Source DP does not exist";
  m["-2"] =	"Destination DP empty";
  m["-3"] =	"Acknowledgement  not possible";
  m["-4"] =	"Deactivation of alert not possible";
  m["-5"] =	"Setting of configs produced an error";
  m["-6"] =	"The query of configs produced an error.";
  m["-7"] =	"The query of the config content produced an error.";
  m["-8"] =	"Creation of the data point failed";
  m["-9"] =	"The target DP already exists.";
  m["-10"] =	"The target data point does not exist.";
  m["-11"] =	"Wrong data point type";
  m["-12"] =	"Source data point is identical to the target data point";

  return m[(string)error];
}



// referenceAddr： 只支持格式 M.1.3.12.2  M.PLCID.FUNCTION_CODE.REFERENCEADDRESS.SUBINDEX
LIB_SetModbusAddress(string dpe,  // dpe
                     string referenceAddr, // dpe address. 格式 M.1.3.12.2  - > 基础地址12，则此处应为12 ，最后是bit位
                     int  driverNumber,
                     string pollingGroup = "_modbus_polling_1s")
{


  //以前是addr地址为空则设置地址禁用。
  // 现在是地址为空则去除掉address属性。
  if ((referenceAddr == "NULL") || (referenceAddr == ""))
  {
    dpSetWait(dpe + ":_distrib.._driver",  DPCONFIG_NONE);
    dpSetWait(dpe + ":_address.._type",  DPCONFIG_NONE);

    return;
  }

  // 先将地址禁用
  dpSetWait(dpe + ":_address.._active", false);

  /*
  _address.. 地址配置项说明：

  _active        地址激活
  _connection    --
  _datatype      数据类型，INT32, FLOAT... 继承自 Master DP
  _direction     读写方向. 继承自 Master DP
  _drv_ident     驱动"MODBUS", "S7"
  _internal      未使用
  _lowlevel      驱动层程序的新旧值比较， 继承自Master DP
  _mode          未使用
  _offset        需要使用的偏移量，如bit偏移量，未使用
  _poll_group    轮询组
  _reference     地址。
  _start         未使用
  _subindex      modbus等需要使用的偏移量，如bit偏移量
  _type          DPCONFIG_PERIPH_ADDR_MAIN 16：物理地址。 DPCONFIG_NONE 0：禁用物理地址。


  _distrib..地址配置项说明：
  _driver       分配驱动号
  _type         驱动激活 DPCONFIG_DISTRIBUTION_INFO 56 driver allocation
  */

  dpSetWait(dpe + ":_distrib.._driver",  driverNumber,
            dpe + ":_distrib.._type",    DPCONFIG_DISTRIBUTION_INFO);



  dyn_string ds = strsplit(referenceAddr, ".");

  if (dynlen(ds) != 5)
  {
    DebugN(__FUNCTION__, "Modbus 地址配置格式有误，应当为M.1.3.12.2这样的格式");
    return;
  }

  string _reference = ds[1] + "." +
                      ds[2] + "." +
                      ds[3] + "." +
                      ds[4] ;

  int _subIndex = (int)ds[5];

  dpSetWait(dpe + ":_address.._reference", _reference,     //"M.1.3.12"
            dpe + ":_address.._subindex", _subIndex,
            dpe + ":_address.._offset", 1,
            dpe + ":_address.._active",   true,
            dpe + ":_address.._poll_group", pollingGroup);


  string _referenceReRead;
  dpGet(dpe + ":_address.._reference", _referenceReRead);

  int _subIndexRead;
  dpGet(dpe + ":_address.._subindex", _subIndexRead);

  string addrReRead = _referenceReRead + "." + (string)_subIndexRead;

  if (addrReRead == referenceAddr)
  {
    DebugN(dpe,   _referenceReRead + "." + (string)_subIndexRead, "OK");
  }
  else
  {
    if (shapeExists("msg"))      msg.append(dpe);

    DebugN(dpe, "期望 " + referenceAddr  + "; 实际 " + _referenceReRead + "." + _subIndexRead, "！！！！！！ERROR ");
  }
}


// 处理没有权限的情况下的弹出对话框, 废弃，仅保留兼容性！
// 以前仅用于在设备下方的按钮进行权限判断。
// 现在不需要了。只返回true。
bool LIB_permissionHandle(int permissionLevel, string dpe)
{
  return true;

}
//删除主点
LIB_mpDeleteMpDp(string dpstr)
{
  string   s;
  s = dpTypeName(dpstr);
  dpDelete(dpstr);

  if (dpExists("_dt_" + s) && dpTypeName("_dt_" + s) == "_DynamicDatapoints")
    dpDelete("_dt_" + s);

}



void LIB_permissionCB(string dp1, string userName)
{
  this.enabled = getUserPermission(1);
}

void LIB_pt_init_PT_navi_sumX(string sumX)
{
  dpConnect("LIB_manageAccess_init_PT_navi_sumq", TRUE, myUiDpName() + ".UserName:_online.._value",
            "_PanelTopology.permissionBit:_online.._value",
            "_PTPanelOn.fileName:_online.._value");
}

void LIB_manageAccess_init_PT_navi_sumq(string dp1, string UserName,
                                        string dp2, dyn_string permissionBits,
                                        string dp3, dyn_string filenames)
{
  LIB_pt_initPanelOnButton(TRUE);
}

void LIB_pt_initPanelOnButton(bool showIcon,
                              string iconShape = "",
                              dyn_string objectsEnDis = makeDynString()) //dynClear(objectsEnDis))
{
  int            i, pos = 0, pNr = $panelNumber, refNr = $refNumber;
  string         pNa = $panelName, dpe;
  dyn_int        alertNumber, diPanelNumber;
  dyn_string     ds, fileName, dsFileName, prioRanges;
  dyn_langString dls;

  //**** mhalper
  dyn_int        diPermissionBits;
  dyn_langString dlsToolTips;
  //****

  string sysName;

  if (globalExists("gSys"))
    sysName = gSys;
  else
    sysName = getSystemName();

  if (strpos(sysName, ":") != -1)
    strreplace(sysName, ":", "");

  pt_checkPanelTopologyCache(sysName);

  fileName = g_PanelTopologyCache[ sysName + ":_PanelTopology.fileName:_online.._value"];
  alertNumber = g_PanelTopologyCache[ sysName + ":_PanelTopology.sumAlertNumber:_online.._value"];
  dls = g_PanelTopologyCache[ sysName + ":_PanelTopology.nodeName:_online.._value"];
  ds = g_PanelTopologyCache[ sysName + ":_PanelTopology.iconName:_online.._value"];
  diPermissionBits = g_PanelTopologyCache[ sysName + ":_PanelTopology.permissionBit:_online.._value"];
  dlsToolTips = g_PanelTopologyCache[ sysName + ":_PanelTopology.description:_online.._value"];

  dpGet(sysName + ":_PTPanelOn.fileName:_online.._value", dsFileName,
        sysName + ":_PTPanelOn.panelNumber:_online.._value", diPanelNumber,
        sysName + ":_SumAlertGeneral.prioRange.name:_online.._value", prioRanges);

  if (refNr > 0 && refNr <= dynlen(dsFileName))
  {
    pNa = dsFileName[refNr];
    pNr = diPanelNumber[refNr];
  }

  pos = pt_getTopologyNumber(fileName, alertNumber, pNa, pNr);

  // set text
  if (pos < 1 || pNa == "" || pNr < 1)
  {
    if (refNr > 0)   // sumX
    {
      dpConnect("pt_manageAccess", sysName + ":_PTPanelOn.fileName:_online.._value",
                sysName + ":_PTPanelOn.panelNumber:_online.._value");
    }

    this.text = ""; return;
  }

  if (this.name == "txtPanelOn")
  {
    this.text = " " + dls[pos];
  }
  else if (this.name == "cmdPanelOn") //**** mhalper: do not write a text on the button if an icon used used
  {
    if (ds[pos] != "")
    {
      this.text = "";
    }
    else
    {
      // 显示按钮的文字
      string newNodenameCN = dls[pos] ;

      // 较高的优先级：用PanalTopologyChinese.cat 找一下翻译。如果有，采用。适合单机版开发（无法做分布式）
      if (strpos(getCatStr("PanalTopologyChinese", dls[pos]), "not found") == -1)  // FOUND!
      {
        newNodenameCN = getCatStr("PanalTopologyChinese", dls[pos]);
      }

      // 最高优先级：去子系统专用的 PanalTopologyChinese_WWT.cat 找一下翻译，如果有，则覆盖之前的设置，最终采纳。
      if (strpos(getCatStr("PanalTopologyChinese" + "_" + gSys, dls[pos]), "not found") == -1)   //  FOUND!
      {
        newNodenameCN = getCatStr("PanalTopologyChinese" + "_" + gSys, dls[pos]);
      }

      this.text = newNodenameCN;

    }
  }
  else
  {
    this.text = dls[pos];
  }

  if (showIcon) // show icon
  {
    if (ds[pos] != "")
    {
      //**** mhalper: include user bits
      if (getUserPermission(diPermissionBits[pos]))
      {
        pt_initPanelOnButton_grap_obj(TRUE, iconShape, "any", ds[pos], dlsToolTips[pos], objectsEnDis);
      }
      else
      {
        if (strreplace(ds[pos], "_enabled.", "_disabled.") < 0)
        {
          strreplace(ds[pos], "_en.", "_dis.");
        }

        pt_initPanelOnButton_grap_obj(FALSE, iconShape, "any", ds[pos], dlsToolTips[pos], objectsEnDis);
      }

      //****
    }
  }

  if (refNr > 0)   // sumX
  {

    dpConnect("pt_manageAccess", sysName + ":_PTPanelOn.fileName:_online.._value",
              sysName + ":_PTPanelOn.panelNumber:_online.._value");
  }
  else // not sumX
  {

    for (i = 1; i <= dynlen(prioRanges); i++)
    {
      dpe = sysName + ":" + pt_fileNameToDpName(pNa, pNr) + "." + prioRanges[i] + ":_alert_hdl.._act_state_color";

      if (dpExists(dpe))
      {
        dpConnect("LIB_pt_setBackColorCB", true, dpe);
      }
    }
  }
}

LIB_pt_setBackColorCB(string dpSource, string sNewColor)
{
  int        refNr = $refNumber, sumNr;
  string     s;
  dyn_string ds = strsplit(dpSource, ":"), prioRanges;

  string sysName;

  if (globalExists("gSys") && (gSys != ""))
  {
    sysName = gSys;
  }
  else
  {
    sysName = getSystemName();
    gSys = sysName;
  }

  if (strpos(sysName, ":") != -1)
    strreplace(sysName, ":", "");

  dpGet(sysName + ":_SumAlertGeneral.prioRange.name:_online.._value", prioRanges);
  dpSource = dpSubStr(dpSource, DPSUB_SYS_DP_EL_CONF_DET_ATT);
  ds = strsplit(dpSource, ":"); s = ds[2];
  ds = strsplit(s, "."); s = ds[2]; sumNr = dynContains(prioRanges, s);

  if (shapeExists("sum" + sumNr))
  {
    setValue("sum" + sumNr, "backCol", sNewColor);
  }
}


LIB_Manual_ISO(string dp)
{

  string dpe = dp + ".ERROR.ISO.VALUE";

  if (!dpExists(dpe))
    return;

  dpConnect("iso_CallBack", dpe);
}

void iso_CallBack(string dp, bool val)
{
  string dpSource = dpSubStr(dp, DPSUB_SYS_DP);
  dyn_string dsDpe = dpNames(dpSource + ".ERROR.*.VALUE");

  if (val)
  {
    btISO.backCol = "LIB_Object_ISO";

    for (int i = 1; i <= dynlen(dsDpe); i++)
    {
      dpSetTimed(0, dsDpe[i] + ":_alert_hdl.._ack", 2);     //  对于_alert_hdl需要试用dpSetTimed来处理！
      dpSetTimed(0, dsDpe[i] + ":_alert_hdl.._active", 0);

      string alertClass, newAlertClass;
      dpGet(dsDpe[i] + ":_alert_hdl.._class", alertClass);

      if (strpos(alertClass, "LIB_Alert_Level1") > 0)
      {
        strreplace(alertClass, "LIB_Alert_Level1", "LIB_Alert_Level01");
      }
      else if (strpos(alertClass, "LIB_Alert_Level2") > 0)
      {
        strreplace(alertClass, "LIB_Alert_Level2", "LIB_Alert_Level02");
      }
      else if (strpos(alertClass, "LIB_Alert_Level3") > 0)
      {
        strreplace(alertClass, "LIB_Alert_Level3", "LIB_Alert_Level03");
      }

      dpSetTimed(0, dsDpe[i] + ":_alert_hdl.._class", alertClass);
      dpSetTimed(0, dsDpe[i] + ":_alert_hdl.._active", 1);
    }
  }
  else
  {
    btISO.backCol = "WF_Frame";

    for (int i = 1; i <= dynlen(dsDpe); i++)
    {
      dpSetTimed(0, dsDpe[i] + ":_alert_hdl.._ack", 2);
      dpSetTimed(0, dsDpe[i] + ":_alert_hdl.._active", 0);

      string alertClass, newAlertClass;
      dpGet(dsDpe[i] + ":_alert_hdl.._class", alertClass);

      if (strpos(alertClass, "LIB_Alert_Level01") > 0)
      {
        strreplace(alertClass, "LIB_Alert_Level01", "LIB_Alert_Level1");
      }
      else if (strpos(alertClass, "LIB_Alert_Level02") > 0)
      {
        strreplace(alertClass, "LIB_Alert_Level02", "LIB_Alert_Level2");
      }
      else if (strpos(alertClass, "LIB_Alert_Level03") > 0)
      {
        strreplace(alertClass, "LIB_Alert_Level03", "LIB_Alert_Level3");
      }

      dpSetTimed(0, dsDpe[i] + ":_alert_hdl.._class", alertClass);
      dpSetTimed(0, dsDpe[i] + ":_alert_hdl.._active", 1);
    }
  }
}

// 获得当前有多少个已经建立连接的分布式系统。
// 返回分布式系统名称的数组。
// auth:zhigang-li@siemens.com
dyn_string LIB_GetConnectedDistSystemNames()
{
  // 获得当前有多少个分布式系统。通过检测*:_Event*.Heartbeat的心跳数值与当前的时间之间的差值来进行判断，准确度比较高。
  dyn_string dsSysHeartbeats = dpNames("*:_Event*.Heartbeat");
  dyn_string dsSysName;

  for (int i_sys_id = 1; i_sys_id <= dynlen(dsSysHeartbeats); i_sys_id++)
  {
    time t;
    dpGet(dsSysHeartbeats[i_sys_id] + ":_original.._stime", t);

    // msg.append( "diag:" + dsSysHeartbeats[i_sys_id]+ " " +(string)t );

    if (getCurrentTime() - t < 10)    // 如果心跳小于10s，说明：1.监测到分布式系统心跳DP，2. 心跳是10s之内的有效的心跳。则判断这个分布式节点是活的。
    {
      string sys = dpSubStr(dsSysHeartbeats[i_sys_id], DPSUB_SYS);  // MEP:_Event.Heartbeat -> sys: MEP:
      dynAppend(dsSysName, sys);
    }
  }

  dynUnique(dsSysName); //可能是冗余的心跳、去重。

  return dsSysName;
}

//表格数据导出至CSV文件
void LIB_TableDataExportToCSV(string tableName, string fileName = formatTime("%Y%m%d%H%M%S", getCurrentTime()))
{

  string sFileTable, strTable;
  dyn_anytype df, ds;

  ChildPanelOnCentralModalReturn("vision/aes/AESdownload.pnl",
                                 getCatStr("ac", "export"),
                                 makeDynString("$fileName:" + fileName), df, ds);

  if (dynlen(df) < 1 || df[1] == 0 || dynlen(ds) < 1)return;

  sFileTable = ds[1];
  setValue(tableName, "writeToFile", sFileTable, TABLE_WRITE_VISIBLE_COLUMNS | TABLE_WRITE_COLUMN_HEADER, ",");



  fileToString(sFileTable, strTable);
  file f = fopen(sFileTable, "w");
  fputs(recode(strTable, "UTF8", "GB2312"), f);
  fclose(f);
}

















